diff --git a/Shaders/raytrace/raytrace_bake.cso b/Shaders/raytrace/raytrace_bake.cso new file mode 100644 index 00000000..41ebc6b3 Binary files /dev/null and b/Shaders/raytrace/raytrace_bake.cso differ diff --git a/Shaders/raytrace/src/build.bat b/Shaders/raytrace/src/build.bat index 323c6517..88d61938 100644 --- a/Shaders/raytrace/src/build.bat +++ b/Shaders/raytrace/src/build.bat @@ -1 +1,2 @@ -.\dxc.exe -Zpr -Fo ..\raytrace.cso -T lib_6_3 .\raytrace.hlsl \ No newline at end of file +.\dxc.exe -Zpr -Fo ..\raytrace.cso -T lib_6_3 .\raytrace.hlsl +.\dxc.exe -Zpr -Fo ..\raytrace_bake.cso -T lib_6_3 .\raytrace_bake.hlsl diff --git a/Shaders/raytrace/src/raytrace_bake.hlsl b/Shaders/raytrace/src/raytrace_bake.hlsl new file mode 100644 index 00000000..948cf1e7 --- /dev/null +++ b/Shaders/raytrace/src/raytrace_bake.hlsl @@ -0,0 +1,129 @@ + +struct Vertex { + float3 position; + float3 normal; + float2 tex; +}; + +struct RayGenConstantBuffer { + float4 v0; // frame + float4 v1; + float4 v2; + float4 v3; + float4 v4; +}; + +struct RayPayload { + float4 color; + float3 ray_origin; + float3 ray_dir; +}; + +RWTexture2D render_target : register(u0); +RaytracingAccelerationStructure scene : register(t0); +ByteAddressBuffer indices : register(t1); +StructuredBuffer vertices : register(t2); +ConstantBuffer constant_buffer : register(b0); + +Texture2D mytexture0 : register(t3); +Texture2D mytexture1 : register(t4); +Texture2D mytexture2 : register(t5); +Texture2D mytexture_env : register(t6); + +static uint seed; + +uint wang_hash(uint seed) { + seed = (seed ^ 61) ^ (seed >> 16); + seed *= 9; + seed = seed ^ (seed >> 4); + seed *= 0x27d4eb2d; + seed = seed ^ (seed >> 15); + return seed; +} + +void compute_seed(uint index, uint iteration, uint depth) { + seed = uint(wang_hash((1 << 31) | (depth << 22) | iteration) ^ wang_hash(index)); +} + +float rand() { + static const float png_01_convert = (1.0f / 4294967296.0f); + seed ^= uint(seed << 13); + seed ^= uint(seed >> 17); + seed ^= uint(seed << 5); + return float(seed * png_01_convert); +} + +float3 cos_weighted_random_hemisphere_direction(float3 n) { + float2 r = float2(rand(), rand()); + float3 uu = normalize(cross(n, float3(0.0, 1.0, 1.0))); + float3 vv = cross(uu, n); + float ra = sqrt(r.y); + float rx = ra * cos(6.2831 * r.x); + float ry = ra * sin(6.2831 * r.x); + float rz = sqrt(1.0 - r.y); + float3 rr = float3(rx * uu + ry * vv + rz * n); + return normalize(rr); +} + +[shader("raygeneration")] +void raygeneration() { + + uint id = DispatchRaysIndex().x + DispatchRaysDimensions().x * DispatchRaysIndex().y; + compute_seed(id, constant_buffer.v0.x, 0); + + float2 xy = DispatchRaysIndex().xy + 0.5f; + xy.x += rand(); // AA + xy.y += rand(); + + float3 pos = mytexture0.Load(uint3(xy, 0)).rgb; + float3 nor = mytexture1.Load(uint3(xy, 0)).rgb; + + float color = 0.0; + + for (int i = 0; i < 4; ++i) { + RayPayload payload; + payload.ray_origin = pos; + payload.ray_dir = cos_weighted_random_hemisphere_direction(nor); + + RayDesc ray; + ray.TMin = 0.01; + ray.TMax = 10.0; + ray.Origin = payload.ray_origin; + ray.Direction = payload.ray_dir; + + TraceRay(scene, RAY_FLAG_FORCE_OPAQUE, ~0, 0, 1, 0, ray, payload); + color += payload.color.r; + + compute_seed(id, constant_buffer.v0.x, i); + } + + color /= 4; + color = 1 - color; + render_target[DispatchRaysIndex().xy] = float4(color.xxx, 0.0f); +} + +float3 hit_world_position() { + return WorldRayOrigin() + RayTCurrent() * WorldRayDirection(); +} + +float3 hit_attribute(float3 vertexAttribute[3], BuiltInTriangleIntersectionAttributes attr) { + return vertexAttribute[0] + + attr.barycentrics.x * (vertexAttribute[1] - vertexAttribute[0]) + + attr.barycentrics.y * (vertexAttribute[2] - vertexAttribute[0]); +} + +float2 hit_attribute2d(float2 vertexAttribute[3], BuiltInTriangleIntersectionAttributes attr) { + return vertexAttribute[0] + + attr.barycentrics.x * (vertexAttribute[1] - vertexAttribute[0]) + + attr.barycentrics.y * (vertexAttribute[2] - vertexAttribute[0]); +} + +[shader("closesthit")] +void closesthit(inout RayPayload payload, in BuiltInTriangleIntersectionAttributes attr) { + payload.color = float4(1,1,1,0); +} + +[shader("miss")] +void miss(inout RayPayload payload) { + payload.color = float4(0,0,0,-1); +} diff --git a/Sources/arm/nodes/MaterialBuilder.hx b/Sources/arm/nodes/MaterialBuilder.hx index 8d9d9916..89272385 100644 --- a/Sources/arm/nodes/MaterialBuilder.hx +++ b/Sources/arm/nodes/MaterialBuilder.hx @@ -52,6 +52,26 @@ class MaterialBuilder { var frag = con_paint.make_frag(); frag.ins = vert.outs; + #if kha_direct3d12 + if (UITrait.inst.bakeType == -1) { + // Init raytraced bake + vert.add_out('vec3 position'); + vert.add_out('vec3 normal'); + vert.write('position = pos.xyz;'); + vert.write('normal = vec3(nor.xy, pos.w);'); + vert.write('vec2 tpos = vec2(tex.x * 2.0 - 1.0, (1.0 - tex.y) * 2.0 - 1.0);'); + vert.write('gl_Position = vec4(tpos, 0.0, 1.0);'); + frag.add_out('vec4 fragColor[2]'); + frag.write('fragColor[0] = vec4(position, 1.0);'); + frag.write('fragColor[1] = vec4(normal, 1.0);'); + + con_paint.data.shader_from_source = true; + con_paint.data.vertex_shader = vert.get(); + con_paint.data.fragment_shader = frag.get(); + return con_paint; + } + #end + if (Context.tool == ToolColorId || Context.tool == ToolPicker) { // Mangle vertices to form full screen triangle vert.write('gl_Position = vec4(-1.0 + float((gl_VertexID & 1) << 2), -1.0 + float((gl_VertexID & 2) << 1), 0.0, 1.0);'); diff --git a/Sources/arm/render/RenderPathDeferred.hx b/Sources/arm/render/RenderPathDeferred.hx index 41b97f8d..b1e6859f 100644 --- a/Sources/arm/render/RenderPathDeferred.hx +++ b/Sources/arm/render/RenderPathDeferred.hx @@ -103,7 +103,7 @@ class RenderPathDeferred { t.name = "taa"; t.width = 0; t.height = 0; - #if kha_direct3d12 + #if kha_direct3d12 // Match raytrace_target format t.format = "RGBA128"; #else t.format = "RGBA32"; @@ -632,16 +632,62 @@ class RenderPathDeferred { if (Context.ddirty > 1 || Context.pdirty > 0) { RenderPathRaytrace.frame = 1.0; } - RenderPathRaytrace.commands(); - path.setTarget("taa"); - drawCompass(path.currentG); - path.setTarget(""); - path.bindTarget("taa", "tex"); - path.drawShader("shader_datas/compositor_pass/compositor_pass"); - if (UITrait.inst.brush3d) { - RenderPathPaint.commandsCursor(); + var bake = true; + if (bake) { + if (path.renderTargets.get("baketex0") == null) { + { + var t = new RenderTargetRaw(); + t.name = "baketex0"; + t.width = Config.getTextureRes(); + t.height = Config.getTextureRes(); + t.format = "RGBA64"; + path.createRenderTarget(t); + } + { + var t = new RenderTargetRaw(); + t.name = "baketex1"; + t.width = Config.getTextureRes(); + t.height = Config.getTextureRes(); + t.format = "RGBA64"; + path.createRenderTarget(t); + } + { + var t = new RenderTargetRaw(); + t.name = "baketex2"; + t.width = Config.getTextureRes(); + t.height = Config.getTextureRes(); + t.format = "RGBA128"; // Match raytrace_target format + path.createRenderTarget(t); + } + } + if (!RenderPathRaytrace.ready) { + var _bakeType = UITrait.inst.bakeType; + UITrait.inst.bakeType = -1; + MaterialParser.parsePaintMaterial(); + + path.setTarget("baketex0", ["baketex1"]); + path.drawMeshes("paint"); + + UITrait.inst.bakeType = _bakeType; + // MaterialParser.parsePaintMaterial(); + } + RenderPathRaytrace.commandsBake(); + path.setTarget("texpaint" + Context.layer.id); + path.bindTarget("baketex2", "tex"); + path.drawShader("shader_datas/copy_pass/copy_pass"); + } + else { + RenderPathRaytrace.commands(); + path.setTarget("taa"); + drawCompass(path.currentG); + path.setTarget(""); + path.bindTarget("taa", "tex"); + path.drawShader("shader_datas/compositor_pass/compositor_pass"); + if (UITrait.inst.brush3d) { + RenderPathPaint.commandsCursor(); + } + return; } - return; #end #end // arm_painter diff --git a/Sources/arm/render/RenderPathRaytrace.hx b/Sources/arm/render/RenderPathRaytrace.hx index 77a2d846..361a17ad 100644 --- a/Sources/arm/render/RenderPathRaytrace.hx +++ b/Sources/arm/render/RenderPathRaytrace.hx @@ -128,6 +128,60 @@ class RenderPathRaytrace { Context.pdirty--; Context.rdirty--; } + + public static function initBake() { + iron.data.Data.getBlob("raytrace_bake.cso", function(shader:kha.Blob) { + if (Context.mergedObject == null) arm.util.MeshUtil.mergeMesh(); + var mo = Context.mergedObject; + var sc = mo.transform.scale; + var md = mo.data; + var geom = md.geom; + var count = Std.int(geom.positions.length / 4); + var vb = new kha.arrays.Float32Array((count + 4) * 8); + for (i in 0...count) { + vb[i * 8 ] = (geom.positions[i * 4 ] / 32767); + vb[i * 8 + 1] = (geom.positions[i * 4 + 1] / 32767); + vb[i * 8 + 2] = (geom.positions[i * 4 + 2] / 32767); + vb[i * 8 + 3] = geom.normals [i * 2 ] / 32767; + vb[i * 8 + 4] = geom.normals [i * 2 + 1] / 32767; + vb[i * 8 + 5] = geom.positions[i * 4 + 3] / 32767; + vb[i * 8 + 6] = (geom.uvs[i * 2 ] / 32767); + vb[i * 8 + 7] = (geom.uvs[i * 2 + 1] / 32767); + } + + var indices = geom.indices[0]; + var ib = new kha.arrays.Uint32Array(indices.length + 6); + for (i in 0...indices.length) ib[i] = indices[i]; + + var path = RenderPathDeferred.path; + var baketex0 = path.renderTargets.get("baketex0").image; + var baketex1 = path.renderTargets.get("baketex1").image; + var baketex2 = path.renderTargets.get("baketex2").image; + var savedEnvmap = Scene.active.world.probe.radiance; + var layer = Context.layer; + + untyped Krom.raytraceInit( + shader.bytes.getData(), vb.buffer, ib.buffer, layer.texpaint.width, layer.texpaint.height, + baketex0.renderTarget_, baketex1.renderTarget_, baketex2.renderTarget_, + savedEnvmap.texture_); + }); + } + + public static function commandsBake() { + if (!ready) { ready = true; initBake(); return; } + f32[0] = frame; + frame += 1.0; + + var path = RenderPathDeferred.path; + var baketex2 = path.renderTargets.get("baketex2").image; + + untyped Krom.raytraceDispatchRays(baketex2.renderTarget_, f32.buffer); + + Context.ddirty = 1; + // Context.ddirty--; + Context.pdirty--; + Context.rdirty--; + } } #end