写通了,存个档
This commit is contained in:
@ -116,7 +116,6 @@ class Scene:
|
||||
else:
|
||||
raise Exception(f"{op_id} is not a valid operation.")
|
||||
|
||||
# 判断是否可以绘制,有点蹩脚但是省事
|
||||
try:
|
||||
sdf_block = obj_instance.sdf_block()
|
||||
self.canvas_objs.append((obj_instance, sdf_block))
|
||||
@ -128,33 +127,36 @@ class Scene:
|
||||
return ", ".join([str(type(obj)) for obj in self.canvas_objs])
|
||||
|
||||
def compile(self) -> str:
|
||||
path = (
|
||||
pathlib.Path("konabot")
|
||||
/ "plugins"
|
||||
/ "sksl"
|
||||
/ "marchtoy"
|
||||
/ "shaders"
|
||||
/ "raymarch.sksl"
|
||||
)
|
||||
with path.open(encoding="utf-8") as f:
|
||||
PATH = pathlib.Path(__file__).parent / "shaders" / "raymarch.sksl"
|
||||
with PATH.open(encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
sdf_block: str = ""
|
||||
color_block: str = ""
|
||||
|
||||
index = 0
|
||||
for canvas_item in self.canvas_objs:
|
||||
sdf_block += f"float sd{index} = {canvas_item[1]};"
|
||||
sdf_block += f"if(qry.value < sd{index})"
|
||||
obj, sdf_expr = canvas_item
|
||||
sdf_block += f"float sd{index} = {sdf_expr};"
|
||||
sdf_block += f"if(sd{index} < qry.value)"
|
||||
sdf_block += "{" + f"qry.value = sd{index}; qry.obj_id = {index}; " + "}\n"
|
||||
color = obj.texture.color
|
||||
color_block += (
|
||||
f"if(obj_id == {index}) return float4("
|
||||
f"{color[0]}, {color[1]}, {color[2]}, {color[3]});\n"
|
||||
)
|
||||
index += 1
|
||||
|
||||
content = content.replace("<SDF_BLOCK>", sdf_block)
|
||||
|
||||
content = content.replace("<COLOR_BLOCK>", color_block)
|
||||
|
||||
cam_pos = self.camera.transform.t[0:3, 3]
|
||||
cam_focus = self.camera.focus
|
||||
cam_dir = self.camera.transform.t @ np.array((0.0, 0.0, -1.0, 0.0))
|
||||
content = content.replace("<CAM_POS>", f"{cam_pos[0]}, {cam_pos[1]}, {cam_pos[2]}")
|
||||
content = content.replace(
|
||||
"<CAM_POS>", f"{cam_pos[0]}, {cam_pos[1]}, {cam_pos[2]}"
|
||||
)
|
||||
content = content.replace("<CAM_FOCUS>", str(cam_focus))
|
||||
content = content.replace("<CAM_DIR>", f"{cam_dir[0]}, {cam_dir[1]}, {cam_dir[2]}")
|
||||
content = content.replace(
|
||||
"<CAM_DIR>", f"{cam_dir[0]}, {cam_dir[1]}, {cam_dir[2]}"
|
||||
)
|
||||
return content
|
||||
|
||||
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
from .obj import Scene
|
||||
from .utilities import format_float, format_float3
|
||||
|
||||
|
||||
TEMPLATE_PATH = Path(__file__).parent / "passes" / "pass1.sksl"
|
||||
TEMPLATE = TEMPLATE_PATH.read_text("utf-8")
|
||||
|
||||
|
||||
def compile_scene_to_shader(scene: Scene) -> str:
|
||||
object_blocks = "\n".join(
|
||||
obj.compile_block(index)
|
||||
for index, obj in enumerate(scene.objects, start=1)
|
||||
)
|
||||
|
||||
return (
|
||||
TEMPLATE.replace("/*<SCENE_OBJECTS>*/", object_blocks)
|
||||
.replace("/*<CAM_POS>*/", ", ".join(format_float(v) for v in scene.camera.position))
|
||||
.replace(
|
||||
"/*<CAM_TARGET>*/",
|
||||
", ".join(format_float(v) for v in scene.camera.target),
|
||||
)
|
||||
.replace("/*<CAM_FOV>*/", format_float(scene.camera.fov))
|
||||
.replace(
|
||||
"/*<LIGHT_DIR>*/",
|
||||
", ".join(format_float(v) for v in scene.light.direction),
|
||||
)
|
||||
.replace(
|
||||
"/*<GAMMA>*/",
|
||||
format_float3((0.454545, 0.454545, 0.454545)),
|
||||
)
|
||||
)
|
||||
@ -9,7 +9,7 @@ def make_obj(name: str, aliases: list[str] = []):
|
||||
def decorator(cls):
|
||||
OBJECT_ENTRIES[name] = cls
|
||||
for alias in aliases:
|
||||
OBJECT_ENTRIES[id] = cls
|
||||
OBJECT_ENTRIES[alias] = cls
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
@ -62,12 +62,13 @@ class Transform:
|
||||
):
|
||||
p = self.t[0:3, 3]
|
||||
q = np.array([x, y, z])
|
||||
yaxis = up
|
||||
zaxis = Transform.normalize(q - p)
|
||||
xaxis = Transform.normalize(np.cross(zaxis, yaxis))
|
||||
xaxis = Transform.normalize(np.cross(zaxis, up))
|
||||
yaxis = Transform.normalize(np.cross(xaxis, zaxis))
|
||||
V = np.array([xaxis, yaxis, zaxis])
|
||||
self.t[:3, :3] = V
|
||||
# 约定本地 -Z 为“朝前”,这样和 shader 中使用的相机射线方向保持一致。
|
||||
self.t[:3, 0] = xaxis
|
||||
self.t[:3, 1] = yaxis
|
||||
self.t[:3, 2] = -zaxis
|
||||
return self
|
||||
|
||||
def p_expr(self) -> str:
|
||||
@ -113,7 +114,7 @@ class Sphere(Object):
|
||||
return f"sdSphere({self.transform.p_expr()}, {self.radius})"
|
||||
|
||||
|
||||
@make_obj("camera")
|
||||
@make_obj("camera", ["cam"])
|
||||
class Camera(Object):
|
||||
def __init__(self, _focus: float = 1.0) -> None:
|
||||
super().__init__()
|
||||
|
||||
@ -36,12 +36,17 @@ def lookat(obj: Object, args: list[str]):
|
||||
@make_operation("color", ["col", "texture"])
|
||||
def color(obj: Object, args: list[str]):
|
||||
try:
|
||||
if len(args) == 3:
|
||||
if len(args) == 1:
|
||||
col = ArgParser.as_literal_color(args)
|
||||
obj.texture.color = (col[0], col[1], col[2], col[3])
|
||||
elif len(args) == 3:
|
||||
col = ArgParser.as_vec3(args)
|
||||
obj.texture.color = (col[0], col[1], col[2], 1.0)
|
||||
elif len(args) == 4:
|
||||
col = ArgParser.as_vec4(args)
|
||||
obj.texture.color = (col[0], col[1], col[2], col[4])
|
||||
obj.texture.color = (col[0], col[1], col[2], col[3])
|
||||
else:
|
||||
raise Exception("unknown color")
|
||||
|
||||
except:
|
||||
raise Exception("unknown color")
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
uniform float u_time;
|
||||
uniform float2 u_resolution;
|
||||
|
||||
const float EPS = 0.001;
|
||||
const float MAX_ITER = 64;
|
||||
const int MAX_ITER = 64;
|
||||
struct sdQuery {
|
||||
float value;
|
||||
int obj_id;
|
||||
}
|
||||
};
|
||||
|
||||
float sdCube(float3 p, float3 b) {
|
||||
p = abs(p) - b;
|
||||
@ -14,36 +13,46 @@ float sdCube(float3 p, float3 b) {
|
||||
}
|
||||
|
||||
float sdSphere(float3 p, float r) {
|
||||
return length(p) - r
|
||||
return length(p) - r;
|
||||
}
|
||||
|
||||
sdQuery sd(float3 p) {
|
||||
sdQuery qry;
|
||||
qry.value = 100000000.0;
|
||||
qry.obj_id = -1;
|
||||
<SDF_BLOCK>
|
||||
return qry;
|
||||
}
|
||||
|
||||
float3 nrm(float3 p) {
|
||||
float2 d = float2(EPS, 0.0);
|
||||
return normalize(float4(
|
||||
sd(p + d.xyy) - sd(p),
|
||||
sd(p + d.yxy) - sd(p),
|
||||
sd(p + d.yyx) - sd(p),
|
||||
return normalize(float3(
|
||||
sd(p + d.xyy).value - sd(p - d.xyy).value,
|
||||
sd(p + d.yxy).value - sd(p - d.yxy).value,
|
||||
sd(p + d.yyx).value - sd(p - d.yyx).value
|
||||
));
|
||||
}
|
||||
|
||||
float4 color(float3 p) {
|
||||
|
||||
return float4(nrm(p), 1.0);
|
||||
float4 materialColor(int obj_id) {
|
||||
<COLOR_BLOCK>
|
||||
return float4(1.0);
|
||||
}
|
||||
|
||||
float4 color(float3 p, int obj_id) {
|
||||
float3 normal = nrm(p);
|
||||
float3 light_dir = normalize(float3(0.5, 0.8, -0.6));
|
||||
float light = 0.2 + 0.8 * max(dot(normal, light_dir), 0.0);
|
||||
float4 base = materialColor(obj_id);
|
||||
return float4(base.rgb * light, base.a);
|
||||
}
|
||||
|
||||
float4 march(float3 p, float3 r) {
|
||||
sdQuery qry;
|
||||
float4 col;
|
||||
float4 col = float4(0.0);
|
||||
for(int i = 0; i < MAX_ITER; ++i) {
|
||||
qry = sd(p);
|
||||
if(qry.value < EPS){
|
||||
col = color(p);
|
||||
col = color(p, qry.obj_id);
|
||||
break;
|
||||
}
|
||||
p += qry.value * r;
|
||||
@ -55,10 +64,10 @@ half4 main(float2 fragCoord) {
|
||||
float2 uv = -2. * (fragCoord / u_resolution - .5) * float2(u_resolution.x / u_resolution.y, 1.);
|
||||
float3 c_p = float3(<CAM_POS>);
|
||||
float3 c_z = normalize(float3(<CAM_DIR>));
|
||||
float3 c_y = float3(0., 1., 0.);
|
||||
float3 c_x = normalize(cross(c_z, c_y));
|
||||
float3 world_up = abs(c_z.y) > 0.999 ? float3(0., 0., 1.) : float3(0., 1., 0.);
|
||||
float3 c_x = normalize(cross(c_z, world_up));
|
||||
float3 c_y = normalize(cross(c_x, c_z));
|
||||
float3x3 view = float3x3(c_x, c_y, c_z);
|
||||
float3 r = normalize(float3(u, v, <CAM_FOCUS>));
|
||||
float3 r = normalize(float3(uv, <CAM_FOCUS>));
|
||||
return half4(march(c_p, view * r));
|
||||
}
|
||||
|
||||
|
||||
39
konabot/plugins/sksl/marchtoy/test.py
Normal file
39
konabot/plugins/sksl/marchtoy/test.py
Normal file
@ -0,0 +1,39 @@
|
||||
from command import Scene
|
||||
import skia
|
||||
import struct
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
# 暂时先照抄小帕的了,之后有空再单独封装一下
|
||||
|
||||
width = 480
|
||||
height = 480
|
||||
scene = Scene("sphere(1).pos(0, 0, 0) camera(4.0).pos(1).lookat(0.0)")
|
||||
surface = skia.Surface(width, height)
|
||||
sksl_code = scene.compile()
|
||||
|
||||
runtime_effect = skia.RuntimeEffect.MakeForShader(scene.compile())
|
||||
if runtime_effect is None:
|
||||
raise Exception("cannot compile sksl shader")
|
||||
|
||||
uv_uniform = struct.pack("ff", float(width), float(height))
|
||||
uniform_data = skia.Data.MakeWithCopy(uv_uniform)
|
||||
shader = runtime_effect.makeShader(uniform_data, None, 0)
|
||||
canvas = surface.getCanvas()
|
||||
canvas.clear(skia.Color(0, 0, 0, 0))
|
||||
paint = skia.Paint()
|
||||
paint.setShader(shader)
|
||||
canvas.drawRect(skia.Rect.MakeWH(width, height), paint)
|
||||
image = surface.makeImageSnapshot()
|
||||
target_info = skia.ImageInfo.Make(
|
||||
image.width(),
|
||||
image.height(),
|
||||
skia.ColorType.kBGRA_8888_ColorType,
|
||||
skia.AlphaType.kPremul_AlphaType,
|
||||
)
|
||||
pixel_data = bytearray(image.width() * image.height() * 4) # 4 bytes per pixel (BGRA)
|
||||
success = image.readPixels(target_info, pixel_data, target_info.minRowBytes())
|
||||
img_array = np.frombuffer(pixel_data, dtype=np.uint8).reshape((height, width, 4))
|
||||
rgb_array = img_array[:, :, [2, 1, 0]]
|
||||
pil_img = Image.fromarray(rgb_array)
|
||||
pil_img.save("1.png")
|
||||
@ -13,7 +13,9 @@ class SkslFormatter:
|
||||
v_3 = ", ".join([str(x) for x in m[3]])
|
||||
return f"float4x4(float4({v_0}), float4({v_1}), float4({v_2}), float4({v_3}))"
|
||||
|
||||
|
||||
"""
|
||||
TODO: 除零出现 nan 情况的单独处理
|
||||
"""
|
||||
class ArgParser:
|
||||
@staticmethod
|
||||
def as_float(args: list[str], default: float = 0.0) -> float:
|
||||
@ -49,8 +51,8 @@ class ArgParser:
|
||||
try:
|
||||
if len(args) == 1:
|
||||
x = float(args[0])
|
||||
return np.array((x, x, x))
|
||||
elif len(args) == 3:
|
||||
return np.array((x, x, x, x))
|
||||
elif len(args) == 4:
|
||||
x = float(args[0])
|
||||
y = float(args[1])
|
||||
z = float(args[2])
|
||||
|
||||
Reference in New Issue
Block a user