diff --git a/Shaders/custom_mat_presets/custom_mat_deferred.frag.glsl b/Shaders/custom_mat_presets/custom_mat_deferred.frag.glsl index 2128746c77..a2c034f1be 100644 --- a/Shaders/custom_mat_presets/custom_mat_deferred.frag.glsl +++ b/Shaders/custom_mat_presets/custom_mat_deferred.frag.glsl @@ -50,7 +50,7 @@ void main() { #ifdef _EmissionShaded fragColor[GBUF_IDX_EMISSION] = vec4(emissionCol, 0.0); #endif - + #ifdef _SSRefraction fragColor[GBUF_IDX_REFRACTION] = vec4(ior, opacity, 0.0, 0.0); #endif diff --git a/Shaders/deferred_light/deferred_light.frag.glsl b/Shaders/deferred_light/deferred_light.frag.glsl index 60c1efc9ba..9c2acaa656 100644 --- a/Shaders/deferred_light/deferred_light.frag.glsl +++ b/Shaders/deferred_light/deferred_light.frag.glsl @@ -8,7 +8,7 @@ #ifdef _Irr #include "std/shirr.glsl" #endif -#ifdef _VoxelAOvar +#ifdef _VoxelShadow #include "std/conetrace.glsl" #endif #ifdef _SSS @@ -21,6 +21,7 @@ uniform sampler2D gbufferD; uniform sampler2D gbuffer0; uniform sampler2D gbuffer1; + #ifdef _gbuffer2 uniform sampler2D gbuffer2; #endif @@ -28,15 +29,17 @@ uniform sampler2D gbuffer1; uniform sampler2D gbufferEmission; #endif -#ifdef _VoxelAOvar -uniform sampler3D voxels; +#ifdef _VoxelGI +uniform sampler2D voxels_diffuse; +uniform sampler2D voxels_specular; #endif -#ifdef _VoxelGITemporal -uniform sampler3D voxelsLast; -uniform float voxelBlend; +#ifdef _VoxelAOvar +uniform sampler2D voxels_ao; #endif -#ifdef _VoxelGICam -uniform vec3 eyeSnap; +#ifdef _VoxelShadow +uniform float clipmaps[voxelgiClipmapCount * 10]; +uniform sampler3D voxels; +uniform sampler3D voxelsSDF; #endif uniform float envmapStrength; @@ -273,26 +276,31 @@ void main() { envl.rgb *= envmapStrength * occspec.x; -#ifdef _VoxelAOvar - - #ifdef _VoxelGICam - vec3 voxpos = (p - eyeSnap) / voxelgiHalfExtents; - #else - vec3 voxpos = p / voxelgiHalfExtents; - #endif - - #ifndef _VoxelAONoTrace - #ifdef _VoxelGITemporal - envl.rgb *= 1.0 - (traceAO(voxpos, n, voxels) * voxelBlend + - traceAO(voxpos, n, voxelsLast) * (1.0 - voxelBlend)); - #else - envl.rgb *= 1.0 - traceAO(voxpos, n, voxels); - #endif - #endif +#ifdef _VoxelGI + fragColor.rgb = textureLod(voxels_diffuse, texCoord, 0.0).rgb * voxelgiDiff * albedo; + if(roughness < 1.0 && occspec.y > 0.0) + fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * voxelgiRefl * occspec.y; +#endif +#ifdef _VoxelAOvar + envl.rgb *= 1.0 - textureLod(voxels_ao, texCoord, 0.0).r; #endif +#ifndef _VoxelGI fragColor.rgb = envl; +#endif + // Show voxels + // vec3 origin = vec3(texCoord * 2.0 - 1.0, 0.99); + // vec3 direction = vec3(0.0, 0.0, -1.0); + // vec4 color = vec4(0.0f); + // for(uint step = 0; step < 400 && color.a < 0.99f; ++step) { + // vec3 point = origin + 0.005 * step * direction; + // color += (1.0f - color.a) * textureLod(voxels, point * 0.5 + 0.5, 0); + // } + // fragColor.rgb += color.rgb; + + // Show SSAO + // fragColor.rgb = texture(ssaotex, texCoord).rrr; #ifdef _SSAO // #ifdef _RTGI @@ -320,19 +328,6 @@ void main() { #endif #endif - // Show voxels - // vec3 origin = vec3(texCoord * 2.0 - 1.0, 0.99); - // vec3 direction = vec3(0.0, 0.0, -1.0); - // vec4 color = vec4(0.0f); - // for(uint step = 0; step < 400 && color.a < 0.99f; ++step) { - // vec3 point = origin + 0.005 * step * direction; - // color += (1.0f - color.a) * textureLod(voxels, point * 0.5 + 0.5, 0); - // } - // fragColor.rgb += color.rgb; - - // Show SSAO - // fragColor.rgb = texture(ssaotex, texCoord).rrr; - #ifdef _Sun vec3 sh = normalize(v + sunDir); float sdotNH = max(0.0, dot(n, sh)); @@ -373,12 +368,10 @@ void main() { #endif #endif - #ifdef _VoxelAOvar #ifdef _VoxelShadow - svisibility *= 1.0 - traceShadow(voxels, voxpos, sunDir); + svisibility *= 1.0 - traceShadow(p, n, voxels, voxelsSDF, sunDir, clipmaps); #endif - #endif - + #ifdef _SSRS // vec2 coords = getProjectedCoord(hitCoord); // vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy); @@ -440,10 +433,10 @@ void main() { #ifdef _Spot , true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight #endif - #ifdef _VoxelAOvar #ifdef _VoxelShadow - , voxels, voxpos - #endif + , voxels + , voxelsSDF + , clipmaps #endif #ifdef _MicroShadowing , occspec.x @@ -500,10 +493,10 @@ void main() { , vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w) // scale , lightsArraySpot[li * 2 + 1].xyz // right #endif - #ifdef _VoxelAOvar #ifdef _VoxelShadow - , voxels, voxpos - #endif + , voxels + , voxelsSDF + , clipmaps #endif #ifdef _MicroShadowing , occspec.x @@ -515,5 +508,13 @@ void main() { } #endif // _Clusters +/* +#ifdef _VoxelRefract +if(opac < 1.0) { + vec3 refraction = traceRefraction(p, n, voxels, v, ior, roughness, eye) * voxelgiRefr; + fragColor.rgb = mix(refraction, fragColor.rgb, opac); +} +#endif +*/ fragColor.a = 1.0; // Mark as opaque } diff --git a/Shaders/deferred_light/deferred_light.json b/Shaders/deferred_light/deferred_light.json index 6a5f13da44..c0c8c44205 100644 --- a/Shaders/deferred_light/deferred_light.json +++ b/Shaders/deferred_light/deferred_light.json @@ -11,20 +11,20 @@ "name": "eye", "link": "_cameraPosition" }, - { - "name": "eyeSnap", - "link": "_cameraPositionSnap", - "ifdef": ["_VoxelGICam"] - }, { "name": "voxelBlend", "link": "_voxelBlend", - "ifdef": ["_VoxelGITemporal"] + "ifdef": ["_VoxelTemporal"] }, { "name": "eyeLook", "link": "_cameraLook" }, + { + "name": "clipmaps", + "link": "_clipmaps", + "ifdef": ["VoxelShadow"] + }, { "name": "invVP", "link": "_inverseViewProjectionMatrix" diff --git a/Shaders/smaa_blend_weight/smaa_blend_weight.frag.glsl b/Shaders/smaa_blend_weight/smaa_blend_weight.frag.glsl index b1239d53f8..6651eaa09f 100644 --- a/Shaders/smaa_blend_weight/smaa_blend_weight.frag.glsl +++ b/Shaders/smaa_blend_weight/smaa_blend_weight.frag.glsl @@ -364,7 +364,7 @@ vec4 SMAABlendingWeightCalculationPS(vec2 texcoord, vec2 pixcoord, // one of the boundaries is enough. weights.rg = SMAACalculateDiagWeights(texcoord, e, subsampleIndices); - // We give priority to diagonals, so if we find a diagonal we skip + // We give piority to diagonals, so if we find a diagonal we skip // horizontal/vertical processing. //SMAA_BRANCH if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 diff --git a/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl b/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl index 91ccb21155..b0a8b56b3f 100644 --- a/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl +++ b/Shaders/ssrefr_pass/ssrefr_pass.frag.glsl @@ -81,6 +81,7 @@ void main() { vec3 n; n.z = 1.0 - abs(enc.x) - abs(enc.y); n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy); + n = normalize(n); vec3 viewNormal = V3 * n; vec3 viewPos = getPosView(viewRay, d, cameraProj); diff --git a/Shaders/std/conetrace.glsl b/Shaders/std/conetrace.glsl index a8c8c0b7fe..c56edae380 100644 --- a/Shaders/std/conetrace.glsl +++ b/Shaders/std/conetrace.glsl @@ -1,7 +1,29 @@ +/* +Copyright (c) 2024 Turánszki János +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ #ifndef _CONETRACE_GLSL_ #define _CONETRACE_GLSL_ +#include "std/voxels_constants.glsl" + // References // https://github.com/Friduric/voxel-cone-tracing // https://github.com/Cigg/Voxel-Cone-Tracing @@ -11,112 +33,303 @@ // http://www.seas.upenn.edu/%7Epcozzi/OpenGLInsights/OpenGLInsights-SparseVoxelization.pdf // https://research.nvidia.com/sites/default/files/publications/GIVoxels-pg2011-authors.pdf -const float MAX_DISTANCE = 1.73205080757 * voxelgiRange; -const float VOXEL_SIZE = (2.0 / voxelgiResolution.x) * voxelgiStep; +const float MAX_DISTANCE = voxelgiRange * 100.0; + +#ifdef _VoxelGI +uniform sampler3D dummy; -// uniform sampler3D voxels; -// uniform sampler3D voxelsLast; +vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCount * 10], const float clipmap_index, const float step_dist, const int precomputed_direction, const vec3 face_offset, const vec3 direction_weight) { + vec4 col = vec4(0.0); + vec3 tc = (P - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution); + vec3 half_texel = vec3(0.5) / voxelgiResolution; + tc = tc * 0.5 + 0.5; + tc = clamp(tc, half_texel, 1.0 - half_texel); + tc.x = (tc.x + precomputed_direction) / (6 + DIFFUSE_CONE_COUNT); + tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount; -// vec3 orthogonal(const vec3 u) { -// // Pass normalized u -// const vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector -// return abs(dot(u, v)) > 0.99999 ? cross(u, vec3(0.0, 1.0, 0.0)) : cross(u, v); -// } + if (precomputed_direction == 0) { + col = direction_weight.x * textureLod(voxels, vec3(tc.x + face_offset.x, tc.y, tc.z), 0) + + direction_weight.y * textureLod(voxels, vec3(tc.x + face_offset.y, tc.y, tc.z), 0) + + direction_weight.z * textureLod(voxels, vec3(tc.x + face_offset.z, tc.y, tc.z), 0); + } + else + col = textureLod(voxels, tc, 0); + + col *= step_dist / float(clipmaps[int(clipmap_index * 10)]); -vec3 tangent(const vec3 n) { - vec3 t1 = cross(n, vec3(0, 0, 1)); - vec3 t2 = cross(n, vec3(0, 1, 0)); - if (length(t1) > length(t2)) return normalize(t1); - else return normalize(t2); + return col; } -float traceConeAO(sampler3D voxels, const vec3 origin, vec3 dir, const float aperture, const float maxDist) { - dir = normalize(dir); - float sampleCol = 0.0; - float dist = 1.5 * VOXEL_SIZE * voxelgiOffset; - float diam = dist * aperture; +#endif +#ifdef _VoxelAOvar +float sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCount * 10], const float clipmap_index, const float step_dist, const int precomputed_direction, const vec3 face_offset, const vec3 direction_weight) { + float opac = 0.0; + vec3 tc = (P - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution); + vec3 half_texel = vec3(0.5) / voxelgiResolution; + tc = tc * 0.5 + 0.5; + tc = clamp(tc, half_texel, 1.0 - half_texel); + tc.x = (tc.x + precomputed_direction) / (6 + DIFFUSE_CONE_COUNT); + tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount; + + if (precomputed_direction == 0) { + opac = direction_weight.x * textureLod(voxels, vec3(tc.x + face_offset.x, tc.y, tc.z), 0).r + + direction_weight.y * textureLod(voxels, vec3(tc.x + face_offset.y, tc.y, tc.z), 0).r + + direction_weight.z * textureLod(voxels, vec3(tc.x + face_offset.z, tc.y, tc.z), 0).r; + } + else + opac = textureLod(voxels, tc, 0).r; + + opac *= step_dist / float(clipmaps[int(clipmap_index * 10)]); + + return opac; +} +#endif + +#ifdef _VoxelGI +vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 origin, const vec3 n, const vec3 dir, const int precomputed_direction, const bool use_sdf, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) { + vec3 color = vec3(0.0); + float alpha = 0.0; + float voxelSize0 = float(clipmaps[0]) * 2.0; + float dist = voxelSize0; + float step_dist = dist; vec3 samplePos; - while (sampleCol < 1.0 && dist < maxDist) { - samplePos = dir * dist + origin; - float mip = max(log2(diam * voxelgiResolution.x), 0); - float mipSample = textureLod(voxels, samplePos * 0.5 + vec3(0.5), mip).r; - sampleCol += (1 - sampleCol) * mipSample; - dist += max(diam / 2, VOXEL_SIZE); - diam = dist * aperture; + vec3 start_pos = origin + n * voxelSize0; + int clipmap_index0 = 0; + + vec3 aniso_direction = -dir; + vec3 face_offset = vec3( + aniso_direction.x > 0 ? 0 : 1, + aniso_direction.y > 0 ? 2 : 3, + aniso_direction.z > 0 ? 4 : 5 + ) / (6 + DIFFUSE_CONE_COUNT); + vec3 direction_weight = abs(dir); + + float coneCoefficient = 2.0 * tan(aperture * 0.5); + + while (alpha < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) { + vec4 mipSample = vec4(0.0); + float diam = max(voxelSize0, dist * coneCoefficient); + float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1); + float clipmap_index = floor(lod); + float clipmap_blend = fract(lod); + vec3 p0 = start_pos + dir * dist; + + samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution); + samplePos = samplePos * 0.5 + 0.5; + + if (any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0)))) { + clipmap_index0++; + continue; + } + + mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, precomputed_direction, face_offset, direction_weight); + + if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) { + vec4 mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight); + mipSample = mix(mipSample, mipSampleNext, clipmap_blend); + } + + float a = 1.0 - alpha; + color += a * mipSample.rgb; + alpha += a * mipSample.a; + + float stepSizeCurrent = step_size; + if (use_sdf) { + // half texel correction is applied to avoid sampling over current clipmap: + const vec3 half_texel = vec3(0.5) / voxelgiResolution; + vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel); + tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap + float sdf = textureLod(voxelsSDF, tc0, 0).r; + stepSizeCurrent = max(step_size, sdf - diam); + } + step_dist = diam * stepSizeCurrent; + dist += step_dist; + } + return vec4(color, alpha); +} + +vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) { + float sum = 0.0; + vec4 amount = vec4(0.0); + for (int i = 0; i < DIFFUSE_CONE_COUNT; ++i) { + vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i]; + const float cosTheta = dot(normal, coneDir); + if (cosTheta <= 0) + continue; + int precomputed_direction = 6 + i; + amount += traceCone(voxels, dummy, origin, normal, coneDir, precomputed_direction, false, DIFFUSE_CONE_APERTURE, 1, clipmaps) * cosTheta; + sum += cosTheta; } - return sampleCol; + amount /= sum; + amount.rgb = max(vec3(0.0), amount.rgb); + amount.a = clamp(amount.a, 0.0, 1.0); + return amount * voxelgiOcc; +} + +vec4 traceSpecular(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 viewDir, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel) { + vec3 specularDir = reflect(-viewDir, normal); + vec3 P = origin;// + specularDir * (BayerMatrix8[int(pixel.x) % 8][int(pixel.y) % 8] - 0.5) * voxelgiStep; + vec4 amount = traceCone(voxels, voxelsSDF, P, normal, specularDir, 0, true, roughness, voxelgiStep, clipmaps); + + amount.rgb = max(vec3(0.0), amount.rgb); + amount.a = clamp(amount.a, 0.0, 1.0); + return amount * voxelgiOcc; } -float traceConeAOShadow(sampler3D voxels, const vec3 origin, vec3 dir, const float aperture, const float maxDist, const float offset) { - dir = normalize(dir); - float sampleCol = 0.0; - float dist = 1.5 * VOXEL_SIZE * voxelgiOffset * 2.5; - float diam = dist * aperture; +/* +vec3 traceRefraction(const vec3 origin, const vec3 normal, sampler3D voxels, sampler3D voxelsSDF, const vec3 viewDir, const float ior, const float roughness, const float clipmaps[voxelgiClipmapCount * 10]) { + const float transmittance = 1.0; + vec3 refractionDir = refract(viewDir, normal, 1.0 / ior); + return transmittance * traceCone(voxels, origin, normal, refractionDir, 0, roughness, voxelgiStep, clipmaps).xyz * voxelgiOcc; +} +*/ +#endif + +#ifdef _VoxelAOvar +float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const vec3 dir, const int precomputed_direction, const float aperture, const float clipmaps[voxelgiClipmapCount * 10]) { + float opacity = 0.0; + float voxelSize0 = float(clipmaps[0]) * 2.0; + float dist = voxelSize0; + float step_dist = dist; vec3 samplePos; - while (sampleCol < 1.0 && dist < maxDist) { - samplePos = dir * dist + origin; - float mip = max(log2(diam * voxelgiResolution.x), 0); - float mipSample = textureLod(voxels, samplePos * 0.5 + vec3(0.5), mip).r; - sampleCol += (1 - sampleCol) * mipSample; - dist += max(diam / 2, VOXEL_SIZE); - diam = dist * aperture; + vec3 start_pos = origin + n * voxelSize0; + int clipmap_index0 = 0; + + vec3 aniso_direction = -dir; + vec3 face_offset = vec3( + aniso_direction.x > 0.0 ? 0 : 1, + aniso_direction.y > 0.0 ? 2 : 3, + aniso_direction.z > 0.0 ? 4 : 5 + ) / (6 + DIFFUSE_CONE_COUNT); + vec3 direction_weight = abs(dir); + + float coneCoefficient = 2.0 * tan(aperture * 0.5); + + while (opacity < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) { + float mipSample = 0.0; + float diam = max(voxelSize0, dist * coneCoefficient); + float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1); + float clipmap_index = floor(lod); + float clipmap_blend = fract(lod); + vec3 p0 = start_pos + dir * dist; + + samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution.x); + samplePos = samplePos * 0.5 + 0.5; + + if ((any(notEqual(clamp(samplePos, 0.0, 1.0), samplePos)))) { + clipmap_index0++; + continue; + } + + mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, precomputed_direction, face_offset, direction_weight); + + if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) { + float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight); + mipSample = mix(mipSample, mipSampleNext, clipmap_blend); + } + + float a = 1.0 - opacity; + opacity += a * mipSample; + + step_dist = diam; + dist += step_dist; } - return sampleCol; + return opacity; } -float traceShadow(sampler3D voxels, const vec3 origin, const vec3 dir) { - return traceConeAO(voxels, origin, dir, 0.14 * voxelgiAperture, 2.5 * voxelgiRange); + +float traceAO(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) { + float sum = 0.0; + float amount = 0.0; + for (int i = 0; i < DIFFUSE_CONE_COUNT; i++) { + vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i]; + int precomputed_direction = 6 + i; + const float cosTheta = dot(normal, coneDir); + if (cosTheta <= 0) + continue; + amount += traceConeAO(voxels, origin, normal, coneDir, precomputed_direction, DIFFUSE_CONE_APERTURE, clipmaps) * cosTheta; + sum += cosTheta; + } + amount /= sum; + amount = max(0.0, amount); + return amount * voxelgiOcc; } +#endif -float traceAO(const vec3 origin, const vec3 normal, sampler3D voxels) { - const float angleMix = 0.5f; - const float aperture = 0.55785173935; - vec3 o1 = normalize(tangent(normal)); - vec3 o2 = normalize(cross(o1, normal)); - vec3 c1 = 0.5f * (o1 + o2); - vec3 c2 = 0.5f * (o1 - o2); - - #ifdef HLSL - const float factor = voxelgiOcc * 0.93; - #else - const float factor = voxelgiOcc * 0.90; - #endif - - #ifdef _VoxelCones1 - return traceConeAO(voxels, origin, normal, aperture, MAX_DISTANCE) * factor; - #endif - - #ifdef _VoxelCones3 - float col = traceConeAO(voxels, origin, normal, aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, o1, angleMix), aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, -c2, angleMix), aperture, MAX_DISTANCE); - return (col / 3.0) * factor; - #endif - - #ifdef _VoxelCones5 - float col = traceConeAO(voxels, origin, normal, aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, o1, angleMix), aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, o2, angleMix), aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, -c1, angleMix), aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, -c2, angleMix), aperture, MAX_DISTANCE); - return (col / 5.0) * factor; - #endif - - #ifdef _VoxelCones9 - float col = traceConeAO(voxels, origin, normal, aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, o1, angleMix), aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, o2, angleMix), aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, -c1, angleMix), aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, -c2, angleMix), aperture, MAX_DISTANCE); - - col += traceConeAO(voxels, origin, mix(normal, -o1, angleMix), aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, -o2, angleMix), aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, c1, angleMix), aperture, MAX_DISTANCE); - col += traceConeAO(voxels, origin, mix(normal, c2, angleMix), aperture, MAX_DISTANCE); - return (col / 9.0) * factor; - #endif - - return 0.0; + +#ifdef _VoxelShadow +float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 origin, const vec3 n, const vec3 dir, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) { + float opacity = 0.0; + float voxelSize0 = float(clipmaps[0]) * 2.0; + float dist = voxelSize0; + float step_dist = dist; + vec3 samplePos; + vec3 start_pos = origin + n * voxelSize0; + int clipmap_index0 = 0; + + vec3 aniso_direction = -dir; + vec3 face_offset = vec3( + aniso_direction.x > 0.0 ? 0 : 1, + aniso_direction.y > 0.0 ? 2 : 3, + aniso_direction.z > 0.0 ? 4 : 5 + ) / (6 + DIFFUSE_CONE_COUNT); + vec3 direction_weight = abs(dir); + + while (opacity < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) { + float mipSample = 0.0; + float diam = max(voxelSize0, dist * 2.0 * tan(aperture * 0.5)); + float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1); + float clipmap_index = floor(lod); + float clipmap_blend = fract(lod); + vec3 p0 = start_pos + dir * dist; + + samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution.x); + samplePos = samplePos * 0.5 + 0.5; + + if ((any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0))))) { + clipmap_index0++; + continue; + } + + #ifdef _VoxelAOvar + mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, 0, face_offset, direction_weight); + #else + mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, 0, face_offset, direction_weight).a; + #endif + + if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) { + #ifdef _VoxelAOvar + float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight); + #else + float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight).a; + #endif + mipSample = mix(mipSample, mipSampleNext, clipmap_blend); + } + + float a = 1.0 - opacity; + opacity += a * mipSample; + + float stepSizeCurrent = step_size; + + if (true) { + // half texel correction is applied to avoid sampling over current clipmap: + const vec3 half_texel = vec3(0.5) / voxelgiResolution; + vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel); + tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap + float sdf = textureLod(voxelsSDF, tc0, 0.0).r; + stepSizeCurrent = max(step_size, sdf - diam); + } + step_dist = diam * stepSizeCurrent; + dist += step_dist; + } + return opacity; } + +float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10]) { + float amount = traceConeShadow(voxels, voxelsSDF, origin, normal, dir, DIFFUSE_CONE_APERTURE, voxelgiStep, clipmaps); + amount = max(0.0, amount); + return amount * voxelgiOcc; +} #endif +#endif // _CONETRACE_GLSL_ diff --git a/Shaders/std/gbuffer.glsl b/Shaders/std/gbuffer.glsl index 07652a50bf..0ded1461ec 100644 --- a/Shaders/std/gbuffer.glsl +++ b/Shaders/std/gbuffer.glsl @@ -101,6 +101,26 @@ vec3 decodeRGBM(const vec4 rgbm) { return rgbm.rgb * rgbm.a * maxRange; } +vec2 signNotZero(vec2 v) +{ + return vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0); +} + +vec2 encode_oct(vec3 v) +{ + // Project the sphere onto the octahedron, and then onto the xy plane + vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z))); + // Reflect the folds of the lower hemisphere over the diagonals + return (v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p; +} + +vec3 decode_oct(vec2 e) +{ + vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y)); + if (v.z < 0) v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy); + return normalize(v); +} + uint encNor(vec3 n) { ivec3 nor = ivec3(n * 255.0f); uvec3 norSigns; diff --git a/Shaders/std/imageatomic.glsl b/Shaders/std/imageatomic.glsl new file mode 100644 index 0000000000..6821934176 --- /dev/null +++ b/Shaders/std/imageatomic.glsl @@ -0,0 +1,84 @@ + +// Courtesy of +// https://github.com/GreatBlambo/voxel_cone_tracing +// https://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-SparseVoxelization.pdf + + +uint convVec4ToRGBA8(vec4 val) { + vec4 col = vec4(val) * 255; + return (uint(col.w) & 0x000000FF) << 24U + | (uint(col.z) & 0x000000FF) << 16U + | (uint(col.y) & 0x000000FF) << 8U + | (uint(col.x) & 0x000000FF); +} + +vec4 convRGBA8ToVec4(uint val) { + uvec4 col = uvec4( + float((val & 0x000000FF)), + float((val & 0x0000FF00) >> 8U), + float((val & 0x00FF0000) >> 16U), + float((val & 0xFF000000) >> 24U)); + return vec4(col) / 255; +} + +// uint encUnsignedNibble(uint m, uint n) { +// return (m & 0xFEFEFEFE) +// | (n & 0x00000001) +// | (n & 0x00000002) << 7U +// | (n & 0x00000004) << 14U +// | (n & 0x00000008) << 21U; +// } + +// uint decUnsignedNibble(uint m) { +// return (m & 0x00000001) +// | (m & 0x00000100) >> 7U +// | (m & 0x00010000) >> 14U +// | (m & 0x01000000) >> 21U; +// } + +// void imageAtomicRGBA8Avg(layout(r32ui) uimage3D img, ivec3 coords, vec4 val) { +// // LSBs are used for the sample counter of the moving average. +// val *= 255.0; +// uint newVal = encUnsignedNibble(convVec4ToRGBA8(val), 1); +// uint prevStoredVal = 0; +// uint currStoredVal; +// int counter = 0; +// // Loop as long as destination value gets changed by other threads +// while ((currStoredVal = imageAtomicCompSwap(img, coords, prevStoredVal, newVal)) != prevStoredVal && counter < 16) { +// vec4 rval = convRGBA8ToVec4(currStoredVal & 0xFEFEFEFE); +// uint n = decUnsignedNibble(currStoredVal); +// rval = rval * n + val; +// rval /= ++n; +// rval = round(rval / 2) * 2; +// newVal = encUnsignedNibble(convVec4ToRGBA8(rval), n); +// prevStoredVal = currStoredVal; +// counter++; +// } +// } + +// void imageAtomicFloatAdd(layout(r32ui) coherent volatile uimage3D imgUI, ivec3 coords, float val) { +// uint newVal = floatBitsToUint(val); +// uint prevVal = 0; +// uint curVal; +// // Loop as long as destination value gets changed by other threads +// while ((curVal = imageAtomicCompSwap(imgUI, coords, prevVal, newVal)) != prevVal) { +// prevVal = curVal; +// newVal = floatBitsToUint((val + uintBitsToFloat(curVal))); +// } +// } + +// void imageAtomicRGBA8Avg( layout ( r32ui ) coherent volatile uimage3D imgUI , ivec3 coords , vec4 val ) { +// val.rgb *= 255.0f; // Optimise following calculations +// uint newVal = convVec4ToRGBA8(val); +// uint prevStoredVal = 0; +// uint curStoredVal; +// // Loop as long as destination value gets changed by other threads +// while ((curStoredVal = imageAtomicCompSwap(imgUI, coords, prevStoredVal, newVal)) != prevStoredVal) { +// prevStoredVal = curStoredVal; +// vec4 rval = convRGBA8ToVec4(curStoredVal); +// rval.xyz = (rval.xyz * rval.w) ; // Denormalize +// vec4 curValF = rval + val; // Add new value +// curValF.xyz /= (curValF.w); // Renormalize +// newVal = convVec4ToRGBA8(curValF); +// } +// } diff --git a/Shaders/std/light.glsl b/Shaders/std/light.glsl index 20a4a7a6d3..b4f15e2194 100644 --- a/Shaders/std/light.glsl +++ b/Shaders/std/light.glsl @@ -7,7 +7,7 @@ #ifdef _ShadowMap #include "std/shadows.glsl" #endif -#ifdef _VoxelAOvar +#ifdef _VoxelShadow #include "std/conetrace.glsl" #endif #ifdef _LTC @@ -89,10 +89,10 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co #ifdef _Spot , bool isSpot, float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right #endif - #ifdef _VoxelAOvar #ifdef _VoxelShadow - , sampler3D voxels, vec3 voxpos - #endif + , sampler3D voxels + , sampler3D voxelsSDF + , float clipmaps[voxelgiClipmapCount * 10] #endif #ifdef _MicroShadowing , float occ @@ -125,6 +125,7 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec; #endif + direct *= attenuate(distance(p, lp)); direct *= lightCol; @@ -136,10 +137,8 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co direct *= traceShadowSS(l, p, gbufferD, invVP, eye); #endif - #ifdef _VoxelAOvar #ifdef _VoxelShadow - direct *= 1.0 - traceShadow(voxels, voxpos, l); - #endif + direct *= 1.0 - traceShadow(p, n, voxels, voxelsSDF, l, clipmaps); #endif #ifdef _LTC diff --git a/Shaders/std/voxels_constants.glsl b/Shaders/std/voxels_constants.glsl new file mode 100644 index 0000000000..b9444d64d8 --- /dev/null +++ b/Shaders/std/voxels_constants.glsl @@ -0,0 +1,55 @@ +/* +Copyright (c) 2024 Turánszki János + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ + +const int DIFFUSE_CONE_COUNT = 16; +const float DIFFUSE_CONE_APERTURE = 0.872665f; + +const vec3 DIFFUSE_CONE_DIRECTIONS[16] = { + vec3(0.57735f, 0.57735f, 0.57735f), + vec3(0.57735f, -0.57735f, -0.57735f), + vec3(-0.57735f, 0.57735f, -0.57735f), + vec3(-0.57735f, -0.57735f, 0.57735f), + vec3(-0.903007f, -0.182696f, -0.388844f), + vec3(-0.903007f, 0.182696f, 0.388844f), + vec3(0.903007f, -0.182696f, 0.388844f), + vec3(0.903007f, 0.182696f, -0.388844f), + vec3(-0.388844f, -0.903007f, -0.182696f), + vec3(0.388844f, -0.903007f, 0.182696f), + vec3(0.388844f, 0.903007f, -0.182696f), + vec3(-0.388844f, 0.903007f, 0.182696f), + vec3(-0.182696f, -0.388844f, -0.903007f), + vec3(0.182696f, 0.388844f, -0.903007f), + vec3(-0.182696f, 0.388844f, 0.903007f), + vec3(0.182696f, -0.388844f, 0.903007f) +}; + +const float BayerMatrix8[8][8] = +{ + { 1.0 / 65.0, 49.0 / 65.0, 13.0 / 65.0, 61.0 / 65.0, 4.0 / 65.0, 52.0 / 65.0, 16.0 / 65.0, 64.0 / 65.0 }, + { 33.0 / 65.0, 17.0 / 65.0, 45.0 / 65.0, 29.0 / 65.0, 36.0 / 65.0, 20.0 / 65.0, 48.0 / 65.0, 32.0 / 65.0 }, + { 9.0 / 65.0, 57.0 / 65.0, 5.0 / 65.0, 53.0 / 65.0, 12.0 / 65.0, 60.0 / 65.0, 8.0 / 65.0, 56.0 / 65.0 }, + { 41.0 / 65.0, 25.0 / 65.0, 37.0 / 65.0, 21.0 / 65.0, 44.0 / 65.0, 28.0 / 65.0, 40.0 / 65.0, 24.0 / 65.0 }, + { 3.0 / 65.0, 51.0 / 65.0, 15.0 / 65.0, 63.0 / 65.0, 2.0 / 65.0, 50.0 / 65.0, 14.0 / 65.0, 62.0 / 65.0 }, + { 35.0 / 65.0, 19.0 / 65.0, 47.0 / 65.0, 31.0 / 65.0, 34.0 / 65.0, 18.0 / 65.0, 46.0 / 65.0, 30.0 / 65.0 }, + { 11.0 / 65.0, 59.0 / 65.0, 7.0 / 65.0, 55.0 / 65.0, 10.0 / 65.0, 58.0 / 65.0, 6.0 / 65.0, 54.0 / 65.0 }, + { 43.0 / 65.0, 27.0 / 65.0, 39.0 / 65.0, 23.0 / 65.0, 42.0 / 65.0, 26.0 / 65.0, 38.0 / 65.0, 22.0 / 65.0 } +}; diff --git a/Shaders/voxel_light/voxel_light.comp.glsl b/Shaders/voxel_light/voxel_light.comp.glsl new file mode 100644 index 0000000000..182c48f2df --- /dev/null +++ b/Shaders/voxel_light/voxel_light.comp.glsl @@ -0,0 +1,145 @@ +#version 450 + +layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in; + +#include "compiled.inc" +#include "std/math.glsl" +#include "std/gbuffer.glsl" +#include "std/imageatomic.glsl" + +uniform vec3 lightPos; +uniform vec3 lightColor; +uniform int lightType; +uniform vec3 lightDir; +uniform vec2 spotData; +#ifdef _ShadowMap +uniform int lightShadow; +uniform vec2 lightProj; +uniform float shadowsBias; +uniform mat4 LVP; +#ifdef _ShadowMapAtlas +uniform int index; +uniform vec4 pointLightDataArray[maxLightsCluster * 6]; +#endif +#endif + +uniform float clipmaps[voxelgiClipmapCount * 10]; +uniform int clipmapLevel; + +uniform layout(r32ui) uimage3D voxelsLight; + +#ifdef _ShadowMap +uniform sampler2DShadow shadowMap; +uniform sampler2DShadow shadowMapSpot; +#ifdef _ShadowMapAtlas +uniform sampler2DShadow shadowMapPoint; +#else +uniform samplerCubeShadow shadowMapPoint; +#endif +#endif + +#ifdef _ShadowMapAtlas +// https://www.khronos.org/registry/OpenGL/specs/gl/glspec20.pdf // p:168 +// https://www.gamedev.net/forums/topic/687535-implementing-a-cube-map-lookup-function/5337472/ +vec2 sampleCube(vec3 dir, out int faceIndex) { + vec3 dirAbs = abs(dir); + float ma; + vec2 uv; + if(dirAbs.z >= dirAbs.x && dirAbs.z >= dirAbs.y) { + faceIndex = dir.z < 0.0 ? 5 : 4; + ma = 0.5 / dirAbs.z; + uv = vec2(dir.z < 0.0 ? -dir.x : dir.x, -dir.y); + } + else if(dirAbs.y >= dirAbs.x) { + faceIndex = dir.y < 0.0 ? 3 : 2; + ma = 0.5 / dirAbs.y; + uv = vec2(dir.x, dir.y < 0.0 ? -dir.z : dir.z); + } + else { + faceIndex = dir.x < 0.0 ? 1 : 0; + ma = 0.5 / dirAbs.x; + uv = vec2(dir.x < 0.0 ? dir.z : -dir.z, -dir.y); + } + // downscale uv a little to hide seams + // transform coordinates from clip space to texture space + #ifndef _FlipY + return uv * 0.9976 * ma + 0.5; + #else + #ifdef HLSL + return uv * 0.9976 * ma + 0.5; + #else + return vec2(uv.x * ma, uv.y * -ma) * 0.9976 + 0.5; + #endif + #endif +} +#endif + +float lpToDepth(vec3 lp, const vec2 lightProj) { + lp = abs(lp); + float zcomp = max(lp.x, max(lp.y, lp.z)); + zcomp = lightProj.x - lightProj.y / zcomp; + return zcomp * 0.5 + 0.5; +} + +void main() { + int res = voxelgiResolution.x; + + ivec3 dst = ivec3(gl_GlobalInvocationID.xyz); + + vec3 P = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution; + P = P * 2.0 - 1.0; + P *= clipmaps[int(clipmapLevel * 10)]; + P *= voxelgiResolution; + P += vec3(clipmaps[int(clipmapLevel * 10 + 4)], clipmaps[int(clipmapLevel * 10 + 5)], clipmaps[int(clipmapLevel * 10 + 6)]); + + vec4 light = vec4(0.0); + + float visibility; + vec3 lp = lightPos - P; + vec3 l; + if (lightType == 0) { l = lightDir; visibility = 1.0; } + else { l = normalize(lp); visibility = attenuate(distance(P, lightPos)); } + + // float dotNL = max(dot(wnormal, l), 0.0); + // if (dotNL == 0.0) return; + +#ifdef _ShadowMap + if (lightShadow == 1) { + vec4 lightPosition = LVP * vec4(P, 1.0); + vec3 lPos = lightPosition.xyz / lightPosition.w; + visibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).r; + } + else if (lightShadow == 2) { + vec4 lightPosition = LVP * vec4(P, 1.0); + vec3 lPos = lightPosition.xyz / lightPosition.w; + visibility *= texture(shadowMapSpot, vec3(lPos.xy, lPos.z - shadowsBias)).r; + } + else if (lightShadow == 3) { + #ifdef _ShadowMapAtlas + int faceIndex = 0; + const int lightIndex = index * 6; + const vec2 uv = sampleCube(-l, faceIndex); + vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas + vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy; + #ifdef _FlipY + uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system + #endif + visibility *= texture(shadowMapPoint, vec3(uvtiled, lpToDepth(lp, lightProj) - shadowsBias)).r; + #else + visibility *= texture(shadowMapPoint, vec4(-l, lpToDepth(lp, lightProj) - shadowsBias)).r; + #endif + } +#endif + + if (lightType == 2) { + float spotEffect = dot(lightDir, l); + if (spotEffect < spotData.x) { + visibility *= smoothstep(spotData.y, spotData.x, spotEffect); + } + } + + light.rgb += visibility * lightColor; + light = clamp(light, vec4(0.0), vec4(1.0)); + + imageAtomicMax(voxelsLight, dst, convVec4ToRGBA8(light)); +} diff --git a/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl b/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl new file mode 100644 index 0000000000..b93e46fda8 --- /dev/null +++ b/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl @@ -0,0 +1,78 @@ +/* +Copyright (c) 2024 Turánszki János + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ +#version 450 + +layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in; + +#include "compiled.inc" +#include "std/math.glsl" +#include "std/gbuffer.glsl" +#include "std/imageatomic.glsl" +#include "std/voxels_constants.glsl" + +#ifdef _VoxelGI +uniform layout(rgba8) image3D voxelsB; +uniform layout(rgba8) image3D voxelsOut; +#else +uniform layout(r8) image3D voxelsB; +uniform layout(r8) image3D voxelsOut; +#endif + +uniform int clipmapLevel; +uniform float voxelBlend; + +uniform float clipmaps[voxelgiClipmapCount * 10]; + +void main() { + const int res = voxelgiResolution.x; + ivec3 src = ivec3(gl_GlobalInvocationID.xyz); + src.y += clipmapLevel * res; + + for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++) + { + vec4 col = vec4(0.0); + + ivec3 dst = src; + dst.x += i * res; + + if (any(notEqual(vec3(clipmaps[clipmapLevel * 10 + 7], clipmaps[clipmapLevel * 10 + 8], clipmaps[clipmapLevel * 10 + 9]), vec3(0.0)))) + { + ivec3 coords = ivec3(dst - vec3(clipmaps[clipmapLevel * 10 + 7], clipmaps[clipmapLevel * 10 + 8], clipmaps[clipmapLevel * 10 + 9])); + int aniso_face_start_x = i * res; + int aniso_face_end_x = aniso_face_start_x + res; + int clipmap_face_start_y = clipmapLevel * res; + int clipmap_face_end_y = clipmap_face_start_y + res; + if ( + coords.x >= aniso_face_start_x && coords.x < aniso_face_end_x && + coords.y >= clipmap_face_start_y && coords.y < clipmap_face_end_y && + coords.z >= 0 && coords.z < res + ) + col = imageLoad(voxelsB, coords); + else + col = vec4(0.0); + } + else + col = imageLoad(voxelsB, dst); + + imageStore(voxelsOut, dst, col); + } +} diff --git a/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl b/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl new file mode 100644 index 0000000000..030e7201d1 --- /dev/null +++ b/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl @@ -0,0 +1,73 @@ +/* +Copyright (c) 2024 Turánszki János + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ + +#version 450 + +layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#include "compiled.inc" +#include "std/math.glsl" +#include "std/gbuffer.glsl" +#include "std/imageatomic.glsl" +#include "std/conetrace.glsl" + +uniform sampler3D voxels; +uniform sampler2D gbufferD; +uniform sampler2D gbuffer0; +uniform layout(r8) image2D voxels_ao; + +uniform float clipmaps[voxelgiClipmapCount * 10]; +uniform mat4 InvVP; +uniform vec2 cameraProj; +uniform vec3 eye; +uniform vec3 eyeLook; +uniform vec2 postprocess_resolution; + +void main() { + const vec2 pixel = gl_GlobalInvocationID.xy; + vec2 uv = (pixel + 0.5) / postprocess_resolution; + #ifdef _InvY + uv.y = 1.0 - uv.y; + #endif + + float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; + if (depth == 0) return; + + float x = uv.x * 2 - 1; + float y = uv.y * 2 - 1; + vec4 v = vec4(x, y, 1.0, 1.0); + v = vec4(InvVP * v); + v.xyz /= v.w; + vec3 viewRay = v.xyz - eye; + + vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj); + + vec4 g0 = textureLod(gbuffer0, uv, 0.0); + vec3 n; + n.z = 1.0 - abs(g0.x) - abs(g0.y); + n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); + n = normalize(n); + + float occ = traceAO(P, n, voxels, clipmaps); + + imageStore(voxels_ao, ivec2(pixel), vec4(occ)); +} diff --git a/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl b/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl new file mode 100644 index 0000000000..b5c9677819 --- /dev/null +++ b/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl @@ -0,0 +1,73 @@ +/* +Copyright (c) 2024 Turánszki János + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ + +#version 450 + +layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#include "compiled.inc" +#include "std/math.glsl" +#include "std/gbuffer.glsl" +#include "std/imageatomic.glsl" +#include "std/conetrace.glsl" + +uniform sampler3D voxels; +uniform sampler2D gbufferD; +uniform sampler2D gbuffer0; +uniform layout(rgba8) image2D voxels_diffuse; + +uniform float clipmaps[voxelgiClipmapCount * 10]; +uniform mat4 InvVP; +uniform vec2 cameraProj; +uniform vec3 eye; +uniform vec3 eyeLook; +uniform vec2 postprocess_resolution; + +void main() { + const vec2 pixel = gl_GlobalInvocationID.xy; + vec2 uv = (pixel + 0.5) / postprocess_resolution; + #ifdef _InvY + uv.y = 1.0 - uv.y + #endif + + float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; + if (depth == 0) return; + + float x = uv.x * 2 - 1; + float y = uv.y * 2 - 1; + vec4 v = vec4(x, y, 1.0, 1.0); + v = vec4(InvVP * v); + v.xyz /= v.w; + vec3 viewRay = v.xyz - eye; + + vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj); + + vec4 g0 = textureLod(gbuffer0, uv, 0.0); + vec3 n; + n.z = 1.0 - abs(g0.x) - abs(g0.y); + n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); + n = normalize(n); + + vec3 color = traceDiffuse(P, n, voxels, clipmaps).rgb; + + imageStore(voxels_diffuse, ivec2(pixel), vec4(color, 1.0)); +} diff --git a/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl b/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl new file mode 100644 index 0000000000..8c19833ba4 --- /dev/null +++ b/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl @@ -0,0 +1,74 @@ +/* +Copyright (c) 2024 Turánszki János + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ + +#version 450 + +layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#include "compiled.inc" +#include "std/math.glsl" +#include "std/gbuffer.glsl" +#include "std/imageatomic.glsl" +#include "std/conetrace.glsl" + +uniform sampler2D gbufferD; +uniform sampler2D gbuffer0; +uniform sampler3D voxels; +uniform sampler3D voxelsSDF; +uniform layout(rgba8) image2D voxels_specular; + +uniform float clipmaps[voxelgiClipmapCount * 10]; +uniform mat4 InvVP; +uniform vec2 cameraProj; +uniform vec3 eye; +uniform vec3 eyeLook; +uniform vec2 postprocess_resolution; + +void main() { + const vec2 pixel = gl_GlobalInvocationID.xy; + vec2 uv = (pixel + 0.5) / postprocess_resolution; + #ifdef _InvY + uv.y = 1.0 - uv.y + #endif + + float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0; + if (depth == 0) return; + + float x = uv.x * 2 - 1; + float y = uv.y * 2 - 1; + vec4 v = vec4(x, y, 1.0, 1.0); + v = vec4(InvVP * v); + v.xyz /= v.w; + vec3 viewRay = v.xyz - eye; + + vec3 P = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj); + + vec4 g0 = textureLod(gbuffer0, uv, 0.0); + vec3 n; + n.z = 1.0 - abs(g0.x) - abs(g0.y); + n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); + n = normalize(n); + + vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), g0.b, clipmaps, pixel).rgb; + + imageStore(voxels_specular, ivec2(pixel), vec4(color, 1.0)); +} diff --git a/Shaders/voxel_sdf_jumpflood/voxel_sdf_jumpflood.comp.glsl b/Shaders/voxel_sdf_jumpflood/voxel_sdf_jumpflood.comp.glsl new file mode 100644 index 0000000000..5e6a0ea729 --- /dev/null +++ b/Shaders/voxel_sdf_jumpflood/voxel_sdf_jumpflood.comp.glsl @@ -0,0 +1,74 @@ +/* +Copyright (c) 2024 Turánszki János + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ +#version 450 + +#include "compiled.inc" + +uniform layout(r8) image3D input_sdf; +uniform layout(r8) image3D output_sdf; + +uniform float jump_size; +uniform int clipmapLevel; +uniform float clipmaps[voxelgiClipmapCount * 10]; + +layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in; + +void main() +{ + int res = voxelgiResolution.x; + int clipmap_start = clipmapLevel * res; + int clipmap_end = clipmap_start + res; + ivec3 src = ivec3(gl_GlobalInvocationID.xyz); + src.y += clipmap_start; + ivec3 dst = src; + + float voxelSize = clipmaps[int(clipmapLevel * 10)]; + + float best_distance = imageLoad(input_sdf, src).r; + + for (int x = -1; x <= 1; ++x) + { + for (int y = -1; y <= 1; ++y) + { + for (int z = -1; z <= 1; ++z) + { + ivec3 offset = ivec3(x, y, z) * int(jump_size); + ivec3 pixel = src + offset; + if ( + pixel.x >= 0 && pixel.x < res && + pixel.y >= clipmap_start && pixel.y < clipmap_end && + pixel.z >= 0 && pixel.z < res + ) + { + float sdf = imageLoad(input_sdf, pixel).r; + float dist = sdf + length(vec3(offset) * voxelSize); + + if (dist < best_distance) + { + best_distance = dist; + } + } + } + } + } + imageStore(output_sdf, dst, vec4(best_distance)); +} diff --git a/Shaders/voxel_temporal/voxel_temporal.comp.glsl b/Shaders/voxel_temporal/voxel_temporal.comp.glsl new file mode 100644 index 0000000000..3789d5a859 --- /dev/null +++ b/Shaders/voxel_temporal/voxel_temporal.comp.glsl @@ -0,0 +1,339 @@ +/* +Copyright (c) 2024 Turánszki János + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ +#version 450 + +#include "compiled.inc" +#include "std/math.glsl" +#include "std/gbuffer.glsl" +#include "std/shadows.glsl" +#include "std/imageatomic.glsl" +#include "std/conetrace.glsl" +#include "std/brdf.glsl" + +#ifdef _VoxelGI +uniform vec3 lightPos; +uniform vec3 lightColor; +uniform int lightType; +uniform vec3 lightDir; +uniform vec2 spotData; +#ifdef _ShadowMap +uniform int lightShadow; +uniform vec2 lightProj; +uniform float shadowsBias; +uniform mat4 LVP; +#endif +uniform sampler3D voxelsSampler; +uniform layout(r32ui) uimage3D voxels; +uniform layout(rgba8) image3D voxelsB; +uniform layout(r32ui) uimage3D voxelsLight; +uniform layout(rgba8) image3D voxelsOut; +#ifdef _ShadowMap +uniform sampler2DShadow shadowMap; +uniform sampler2DShadow shadowMapSpot; +uniform samplerCubeShadow shadowMapPoint; +#endif +#include "std/shirr.glsl" +uniform float envmapStrength; +#ifdef _Irr +uniform vec4 shirr[7]; +#endif +#ifdef _Brdf +uniform sampler2D senvmapBrdf; +#endif +#ifdef _Rad +uniform sampler2D senvmapRadiance; +uniform int envmapNumMipmaps; +#endif +#ifdef _EnvCol +uniform vec3 backgroundCol; +#endif +uniform sampler2D gbufferD; +uniform sampler2D gbuffer0; +uniform sampler2D gbuffer1; +#ifdef _gbuffer2 +#ifdef _Deferred +uniform sampler2D gbuffer2; +#endif +#endif +uniform vec3 eye; +uniform layout(r8) image3D SDF; +#else +#ifdef _VoxelAOvar +#ifdef _VoxelShadow +uniform layout(r8) image3D SDF; +#endif +uniform layout(r32ui) uimage3D voxels; +uniform layout(r8) image3D voxelsB; +uniform layout(r8) image3D voxelsOut; +#endif +#endif + +uniform int clipmapLevel; +uniform float clipmaps[voxelgiClipmapCount * 10]; + +layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in; + +void main() { + int res = voxelgiResolution.x; + #ifdef _VoxelGI + vec4 aniso_colors[6]; + #else + float opac; + float aniso_colors[6]; + #endif + + #ifdef _VoxelGI + float sdf = float(clipmaps[int(clipmapLevel * 10)]) * 2.0 * res; + #else + #ifdef _VoxelShadow + float sdf = float(clipmaps[int(clipmapLevel * 10)]) * 2.0 * res; + #endif + #endif + + #ifdef _VoxelGI + vec4 light = convRGBA8ToVec4(imageLoad(voxelsLight, ivec3(gl_GlobalInvocationID.xyz)).r); + #endif + + for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++) + { + ivec3 src = ivec3(gl_GlobalInvocationID.xyz); + src.x += i * res; + ivec3 dst = src; + dst.y += clipmapLevel * res; + #ifdef _VoxelGI + vec4 radiance = vec4(0.0); + #else + float opac = 0.0; + #endif + + if (i < 6) { + #ifdef _VoxelGI + vec4 basecol = vec4(0.0); + basecol.r = float(imageLoad(voxels, src)) / 255; + basecol.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x))) / 255; + basecol.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 2))) / 255; + basecol.a = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 3))) / 255; + basecol /= 4; + vec3 emission = vec3(0.0); + emission.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 4))) / 255; + emission.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 5))) / 255; + emission.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 6))) / 255; + emission /= 3; + vec3 N = vec3(0.0); + N.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 7))) / 255; + N.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 8))) / 255; + N /= 2; + vec3 wnormal = decode_oct(N.rg * 2 - 1); + + //clipmap to world + vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x; + wposition = wposition * 2.0 - 1.0; + wposition *= float(clipmaps[int(clipmapLevel * 10)]); + wposition *= voxelgiResolution.x; + wposition += vec3(clipmaps[clipmapLevel * 10 + 4], clipmaps[clipmapLevel * 10 + 5], clipmaps[clipmapLevel * 10 + 6]); + + #ifdef _Deferred + const vec2 pixel = gl_GlobalInvocationID.xy; + const vec2 uv = (pixel + 0.5) / voxelgiResolution.xy; + + vec4 g0 = textureLod(gbuffer0, uv, 0.0); // Normal.xy, roughness, metallic/matid + vec3 n; + n.z = 1.0 - abs(g0.x) - abs(g0.y); + n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); + n = normalize(n); + + float roughness = g0.b; + float metallic; + uint matid; + unpackFloatInt16(g0.a, metallic, matid); + + vec4 g1 = textureLod(gbuffer1, uv, 0.0); // Basecolor.rgb, spec/occ + vec2 occspec = unpackFloat2(g1.a); + vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor + vec3 f0 = surfaceF0(g1.rgb, metallic); + + vec3 v = normalize(eye - wposition); + float dotNV = max(dot(wnormal, v), 0.0); + + #ifdef _gbuffer2 + vec4 g2 = textureLod(gbuffer2, uv, 0.0); + #endif + + #ifdef _Brdf + vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy; + #endif + + // Envmap + #ifdef _Irr + vec3 envl = shIrradiance(wnormal, shirr); + + #ifdef _gbuffer2 + if (g2.b < 0.5) { + envl = envl; + } else { + envl = vec3(0.0); + } + #endif + + #ifdef _EnvTex + envl /= PI; + #endif + #else + vec3 envl = vec3(0.0); + #endif + + #ifdef _Rad + vec3 reflectionWorld = reflect(-v, wnormal); + float lod = getMipFromRoughness(roughness, envmapNumMipmaps); + vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb; + #endif + + #ifdef _EnvLDR + envl.rgb = pow(envl.rgb, vec3(2.2)); + #ifdef _Rad + prefilteredColor = pow(prefilteredColor, vec3(2.2)); + #endif + #endif + + envl.rgb *= albedo; + + #ifdef _Brdf + envl.rgb *= 1.0 - (f0 * envBRDF.x + envBRDF.y); + #endif + + #ifdef _Rad // Indirect specular + envl.rgb += prefilteredColor * (f0 * envBRDF.x + envBRDF.y); + #else + #ifdef _EnvCol + envl.rgb += backgroundCol * (f0 * envBRDF.x + envBRDF.y); + #endif + #endif + + envl.rgb *= envmapStrength * occspec.x; + #else + vec3 envl = vec3(0.0); + #endif + + radiance = basecol; + + vec4 trace = traceDiffuse(wposition, wnormal, voxelsSampler, clipmaps); + vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a); + radiance.rgb *= light.rgb + indirect.rgb; + radiance.rgb += emission.rgb; + + #else + opac = float(imageLoad(voxels, src)) / 255; + #endif + + #ifdef _VoxelGI + if (radiance.a > 0) + #else + if (opac > 0) + #endif + { + if (any(notEqual(vec3(clipmaps[clipmapLevel * 10 + 7], clipmaps[clipmapLevel * 10 + 8], clipmaps[clipmapLevel * 10 + 9]), vec3(0.0)))) + { + ivec3 coords = ivec3(dst - vec3(clipmaps[clipmapLevel * 10 + 7], clipmaps[clipmapLevel * 10 + 8], clipmaps[clipmapLevel * 10 + 9])); + int aniso_face_start_x = i * res; + int aniso_face_end_x = aniso_face_start_x + res; + int clipmap_face_start_y = clipmapLevel * res; + int clipmap_face_end_y = clipmap_face_start_y + res; + if ( + coords.x >= aniso_face_start_x && coords.x < aniso_face_end_x && + coords.y >= clipmap_face_start_y && coords.y < clipmap_face_end_y && + coords.z >= 0 && coords.z < res + ) + #ifdef _VoxelGI + radiance = mix(imageLoad(voxelsB, dst), radiance, 0.5); + #else + opac = mix(imageLoad(voxelsB, dst).r, opac, 0.5); + #endif + } + else + #ifdef _VoxelGI + radiance = mix(imageLoad(voxelsB, dst), radiance, 0.5); + #else + opac = mix(imageLoad(voxelsB, dst).r, opac, 0.5); + #endif + } + else + #ifdef _VoxelGI + radiance = vec4(0.0); + #else + opac = 0.0; + #endif + #ifdef _VoxelGI + aniso_colors[i] = radiance; + if (radiance.a > 0) + sdf = 0.0; + #else + aniso_colors[i] = opac; + #ifdef _VoxelShadow + if (opac > 0) + sdf = 0.0; + #endif + #endif + } + else { + // precompute cone sampling: + vec3 coneDirection = DIFFUSE_CONE_DIRECTIONS[i - 6]; + vec3 aniso_direction = -coneDirection; + uvec3 face_offsets = uvec3( + aniso_direction.x > 0 ? 0 : 1, + aniso_direction.y > 0 ? 2 : 3, + aniso_direction.z > 0 ? 4 : 5 + ); + vec3 direction_weights = abs(coneDirection); + #ifdef _VoxelGI + vec4 sam = + aniso_colors[face_offsets.x] * direction_weights.x + + aniso_colors[face_offsets.y] * direction_weights.y + + aniso_colors[face_offsets.z] * direction_weights.z + ; + radiance = sam; + #else + float sam = + aniso_colors[face_offsets.x] * direction_weights.x + + aniso_colors[face_offsets.y] * direction_weights.y + + aniso_colors[face_offsets.z] * direction_weights.z + ; + opac = sam; + #endif + } + #ifdef _VoxelGI + imageStore(voxelsOut, dst, radiance); + #else + imageStore(voxelsOut, dst, vec4(opac)); + #endif + } + #ifdef _VoxelGI + ivec3 dst_sdf = ivec3(gl_GlobalInvocationID.xyz); + dst_sdf.y += clipmapLevel * res; + imageStore(SDF, dst_sdf, vec4(sdf)); + #else + #ifdef _VoxelShadow + ivec3 dst_sdf = ivec3(gl_GlobalInvocationID.xyz); + dst_sdf.y += clipmapLevel * res; + imageStore(SDF, dst_sdf, vec4(sdf)); + #endif + #endif +} diff --git a/Sources/armory/object/Uniforms.hx b/Sources/armory/object/Uniforms.hx index 7273a50d07..5d63c7f4ba 100644 --- a/Sources/armory/object/Uniforms.hx +++ b/Sources/armory/object/Uniforms.hx @@ -5,6 +5,8 @@ import iron.object.Object; import iron.data.MaterialData; import iron.math.Vec4; +import kha.arrays.Float32Array; + import armory.renderpath.Postprocess; using StringTools; @@ -18,7 +20,8 @@ class Uniforms { iron.object.Uniforms.externalVec3Links = [vec3Link]; iron.object.Uniforms.externalVec4Links = []; iron.object.Uniforms.externalFloatLinks = [floatLink]; - iron.object.Uniforms.externalIntLinks = []; + iron.object.Uniforms.externalFloatsLinks = [floatsLink]; + iron.object.Uniforms.externalIntLinks = [intLink]; } public static function textureLink(object: Object, mat: MaterialData, link: String): Null { @@ -166,22 +169,7 @@ class Uniforms { } } #end - - #if rp_voxels - case "_cameraPositionSnap": { - v = iron.object.Uniforms.helpVec; - var camera = iron.Scene.active.camera; - v.set(camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz()); - var l = camera.lookWorld(); - var e = Main.voxelgiHalfExtents; - v.x += l.x * e * 0.9; - v.y += l.y * e * 0.9; - var f = Main.voxelgiVoxelSize * 8; // Snaps to 3 mip-maps range - v.set(Math.floor(v.x / f) * f, Math.floor(v.y / f) * f, Math.floor(v.z / f) * f); - } - #end } - return v; } @@ -214,12 +202,6 @@ class Uniforms { return armory.trait.internal.DebugConsole.debugFloat; } #end - #if rp_voxels - case "_voxelBlend": { // Blend current and last voxels - var freq = armory.renderpath.RenderPathCreator.voxelFreq; - return (armory.renderpath.RenderPathCreator.voxelFrame % freq) / freq; - } - #end #if rp_bloom case "_bloomSampleScale": { return Postprocess.bloom_uniforms[3]; @@ -228,4 +210,40 @@ class Uniforms { } return null; } + + public static function floatsLink(object: Object, mat: MaterialData, link: String): Float32Array { + switch (link) { + #if (rp_voxels != "Off") + case "_clipmaps": { + var clipmaps = iron.RenderPath.clipmaps; + var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10); + for (i in 0...Main.voxelgiClipmapCount) { + fa[i * 10] = clipmaps[i].voxelSize; + fa[i * 10 + 1] = clipmaps[i].extents.x; + fa[i * 10 + 2] = clipmaps[i].extents.y; + fa[i * 10 + 3] = clipmaps[i].extents.z; + fa[i * 10 + 4] = clipmaps[i].center.x; + fa[i * 10 + 5] = clipmaps[i].center.y; + fa[i * 10 + 6] = clipmaps[i].center.z; + fa[i * 10 + 7] = clipmaps[i].offset_prev.x; + fa[i * 10 + 8] = clipmaps[i].offset_prev.y; + fa[i * 10 + 9] = clipmaps[i].offset_prev.z; + } + return fa; + } + #end + } + return null; + } + + public static function intLink(object: Object, mat: MaterialData, link: String): Null { + switch (link) { + #if (rp_voxels != "Off") + case "_clipmapLevel": { + return iron.RenderPath.clipmapLevel; + } + #end + } + return null; + } } diff --git a/Sources/armory/renderpath/Inc.hx b/Sources/armory/renderpath/Inc.hx index ecb235ba56..a5ddb22e05 100644 --- a/Sources/armory/renderpath/Inc.hx +++ b/Sources/armory/renderpath/Inc.hx @@ -4,9 +4,9 @@ import iron.RenderPath; import iron.object.LightObject; import armory.math.Helper; +import kha.arrays.Float32Array; class Inc { - static var path: RenderPath; public static var superSample = 1.0; @@ -14,10 +14,114 @@ class Inc { static var spotIndex = 0; static var lastFrame = -1; - #if (rp_voxels && arm_config) + #if ((rp_voxels != 'Off') && arm_config) static var voxelsCreated = false; #end + #if (rp_voxels != "Off") + static var voxel_sh0:kha.compute.Shader = null; + static var voxel_sh1:kha.compute.Shader = null; + static var voxel_ta0:kha.compute.TextureUnit; + static var voxel_tb0:kha.compute.TextureUnit; + static var voxel_ca0:kha.compute.ConstantLocation; + static var voxel_cb0:kha.compute.ConstantLocation; + static var voxel_ta1:kha.compute.TextureUnit; + static var voxel_tb1:kha.compute.TextureUnit; + static var voxel_tc1:kha.compute.TextureUnit; + static var m = iron.math.Mat4.identity(); + static var voxel_ca1:kha.compute.ConstantLocation; + static var voxel_cb1:kha.compute.ConstantLocation; + #if (rp_voxels == "Voxel GI") + static var voxel_td1:kha.compute.TextureUnit; + static var voxel_te1:kha.compute.TextureUnit; + static var voxel_tf1:kha.compute.TextureUnit; + static var voxel_tg1:kha.compute.TextureUnit; + static var voxel_th1:kha.compute.TextureUnit; + #if (rp_gbuffer2 && arm_deferred) + static var voxel_ti1:kha.compute.TextureUnit; + #end + #if arm_brdf + static var voxel_tj1:kha.compute.TextureUnit; + #end + #if arm_radiance + static var voxel_tk1:kha.compute.TextureUnit; + static var voxel_ce1:kha.compute.ConstantLocation; + #end + #if arm_irradiance + static var voxel_cc1:kha.compute.ConstantLocation; + #end + static var voxel_cd1:kha.compute.ConstantLocation; + #if arm_envldr + static var voxel_cf1:kha.compute.ConstantLocation; + #end + static var voxel_cg1:kha.compute.ConstantLocation; + #else + #if arm_voxelgi_shadows + static var voxel_tf1:kha.compute.TextureUnit; + #end + #end + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + static var voxel_sh2:kha.compute.Shader = null; + static var voxel_ta2:kha.compute.TextureUnit; + static var voxel_tb2:kha.compute.TextureUnit; + static var voxel_ca2:kha.compute.ConstantLocation; + static var voxel_cb2:kha.compute.ConstantLocation; + static var voxel_cc2:kha.compute.ConstantLocation; + #end + #if arm_deferred + static var voxel_sh3:kha.compute.Shader = null; + static var voxel_ta3:kha.compute.TextureUnit; + static var voxel_tb3:kha.compute.TextureUnit; + static var voxel_tc3:kha.compute.TextureUnit; + static var voxel_td3:kha.compute.TextureUnit; + static var voxel_ca3:kha.compute.ConstantLocation; + static var voxel_cb3:kha.compute.ConstantLocation; + static var voxel_cc3:kha.compute.ConstantLocation; + static var voxel_cd3:kha.compute.ConstantLocation; + static var voxel_ce3:kha.compute.ConstantLocation; + static var voxel_cf3:kha.compute.ConstantLocation; + #if (rp_voxels == "Voxel GI") + static var voxel_sh4:kha.compute.Shader = null; + static var voxel_ta4:kha.compute.TextureUnit; + static var voxel_tb4:kha.compute.TextureUnit; + static var voxel_tc4:kha.compute.TextureUnit; + static var voxel_td4:kha.compute.TextureUnit; + static var voxel_te4:kha.compute.TextureUnit; + static var voxel_ca4:kha.compute.ConstantLocation; + static var voxel_cb4:kha.compute.ConstantLocation; + static var voxel_cc4:kha.compute.ConstantLocation; + static var voxel_cd4:kha.compute.ConstantLocation; + static var voxel_ce4:kha.compute.ConstantLocation; + static var voxel_cf4:kha.compute.ConstantLocation; + #end + #end + #if (rp_voxels == "Voxel GI") + static var voxel_sh5:kha.compute.Shader = null; + static var voxel_ta5:kha.compute.TextureUnit; + static var voxel_ca5:kha.compute.ConstantLocation; + static var voxel_cb5:kha.compute.ConstantLocation; + static var voxel_cc5:kha.compute.ConstantLocation; + static var voxel_cd5:kha.compute.ConstantLocation; + static var voxel_ce5:kha.compute.ConstantLocation; + static var voxel_cf5:kha.compute.ConstantLocation; + static var voxel_cg5:kha.compute.ConstantLocation; + #if rp_shadowmap + static var voxel_tb5:kha.compute.TextureUnit; + static var voxel_tc5:kha.compute.TextureUnit; + static var voxel_td5:kha.compute.TextureUnit; + static var voxel_ch5:kha.compute.ConstantLocation; + static var voxel_ci5:kha.compute.ConstantLocation; + static var voxel_cj5:kha.compute.ConstantLocation; + static var voxel_ck5:kha.compute.ConstantLocation; + static var voxel_cl5:kha.compute.ConstantLocation; + static var voxel_cm5:kha.compute.ConstantLocation; + #if arm_shadowmap_atlas + static var m2 = iron.math.Mat4.identity(); + #end + #end + #end + #end //rp_voxels + public static function init(_path: RenderPath) { path = _path; @@ -356,7 +460,7 @@ class Inc { path.resize(); } // Init voxels - #if rp_voxels + #if (rp_voxels != 'Off') if (!voxelsCreated) initGI(); #end #end // arm_config @@ -404,6 +508,12 @@ class Inc { #end } #end + #if (rp_voxels != "Off") + path.bindTarget("voxelsOut", "voxels"); + #if (rp_voxels == "Voxel GI") + path.bindTarget("voxelsSDF", "voxelsSDF"); + #end + #end path.drawMeshes("translucent"); #if rp_render_to_texture { @@ -442,40 +552,84 @@ class Inc { } #end - #if rp_voxels + #if (rp_voxels != 'Off') public static function initGI(tname = "voxels") { + var t = new RenderTargetRaw(); + t.name = tname; + #if arm_config var config = armory.data.Config.raw; - if (config.rp_gi != true || voxelsCreated) return; + if (config.rp_voxels != true || voxelsCreated) return; voxelsCreated = true; #end - var t = new RenderTargetRaw(); - t.name = tname; - t.format = "R8"; + var res = iron.RenderPath.getVoxelRes(); + var resZ = iron.RenderPath.getVoxelResZ(); - var res = getVoxelRes(); - var resZ = getVoxelResZ(); - t.width = res; - t.height = res; - t.depth = Std.int(res * resZ); - t.is_image = true; + if (t.name == "voxels_diffuse" || t.name == "voxels_specular") { + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.scale = getSuperSampling(); + t.format = getHdrFormat(); + } + else if (t.name == "voxels_ao") { + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.scale = getSuperSampling(); + t.format = "R8"; + } + else { + if (t.name == "voxelsSDF" || t.name == "voxelsSDFtmp") { + t.format = "R8"; + t.width = res; + t.height = res * Main.voxelgiClipmapCount; + t.depth = res; + } + else { + #if (rp_voxels == "Voxel AO") + { + if (t.name == "voxelsOut" || t.name == "voxelsOutB") { + t.format = "R8"; + t.width = res * (6 + 16); + t.height = res * Main.voxelgiClipmapCount; + t.depth = res; + } + else { + t.format = "R32"; + t.width = res * 6; + t.height = res; + t.depth = res; + } + } + #else + { + if (t.name == "voxelsOut" || t.name == "voxelsOutB") { + t.width = res * (6 + 16); + t.height = res * Main.voxelgiClipmapCount; + t.format = "RGBA32"; + t.depth = res; + } + else if (t.name == "voxelsLight") { + t.width = res; + t.height = res; + t.format = "R32"; + t.depth = res; + } + else { + t.width = res * 6; + t.height = res; + t.format = "R32"; + t.depth = res * 9; + } + } + #end + } + } t.mipmaps = true; + t.is_image = true; path.createRenderTarget(t); - - #if arm_voxelgi_temporal - { - var tB = new RenderTargetRaw(); - tB.name = t.name + "B"; - tB.format = t.format; - tB.width = t.width; - tB.height = t.height; - tB.depth = t.depth; - tB.is_image = t.is_image; - tB.mipmaps = t.mipmaps; - path.createRenderTarget(tB); - } - #end } #end @@ -515,36 +669,6 @@ class Inc { #end } - public static inline function getVoxelRes(): Int { - #if (rp_voxelgi_resolution == 512) - return 512; - #elseif (rp_voxelgi_resolution == 256) - return 256; - #elseif (rp_voxelgi_resolution == 128) - return 128; - #elseif (rp_voxelgi_resolution == 64) - return 64; - #elseif (rp_voxelgi_resolution == 32) - return 32; - #else - return 0; - #end - } - - public static inline function getVoxelResZ(): Float { - #if (rp_voxelgi_resolution_z == 1.0) - return 1.0; - #elseif (rp_voxelgi_resolution_z == 0.5) - return 0.5; - #elseif (rp_voxelgi_resolution_z == 0.25) - return 0.25; - #elseif (rp_voxelgi_resolution_z == 0.125) - return 0.125; - #else - return 0.0; - #end - } - public static inline function getSuperSampling(): Float { return superSample; } @@ -583,6 +707,650 @@ class Inc { static function endShadowsRenderProfile() { shadowsRenderTime += kha.Scheduler.realTime() - startShadowsRenderTime; } public static function endFrame() { shadowsLogicTime = 0; shadowsRenderTime = 0; } #end + + #if (rp_voxels != "Off") + public static function computeVoxelsBegin() { + if (voxel_sh0 == null) + { + voxel_sh0 = path.getComputeShader("voxel_offsetprev"); + + voxel_ta0 = voxel_sh0.getTextureUnit("voxelsB"); + voxel_tb0 = voxel_sh0.getTextureUnit("voxelsOut"); + + voxel_ca0 = voxel_sh0.getConstantLocation("clipmaps"); + voxel_cb0 = voxel_sh0.getConstantLocation("clipmapLevel"); + } + if (voxel_sh1 == null) + { + voxel_sh1 = path.getComputeShader("voxel_temporal"); + voxel_ta1 = voxel_sh1.getTextureUnit("voxels"); + voxel_tb1 = voxel_sh1.getTextureUnit("voxelsB"); + voxel_tc1 = voxel_sh1.getTextureUnit("voxelsOut"); + + voxel_ca1 = voxel_sh1.getConstantLocation("clipmaps"); + voxel_cb1 = voxel_sh1.getConstantLocation("clipmapLevel"); + + #if (rp_voxels == "Voxel GI") + voxel_td1 = voxel_sh1.getTextureUnit("voxelsSampler"); + voxel_te1 = voxel_sh1.getTextureUnit("voxelsLight"); + voxel_tf1 = voxel_sh1.getTextureUnit("SDF"); + + voxel_tg1 = voxel_sh1.getTextureUnit("gbuffer0"); + voxel_th1 = voxel_sh1.getTextureUnit("gbuffer1"); + #if (rp_gbuffer2 && arm_deferred) + voxel_ti1 = voxel_sh1.getTextureUnit("gbuffer2"); + #end + #if arm_brdf + voxel_tj1 = voxel_sh1.getTextureUnit("senvmapBrdf"); + #end + #if arm_radiance + voxel_tk1 = voxel_sh1.getTextureUnit("senvmapRadiance"); + voxel_ce1 = voxel_sh1.getConstantLocation("envmapNumMipmaps"); + #end + #if arm_irradiance + voxel_cc1 = voxel_sh1.getConstantLocation("shirr"); + #end + voxel_cd1 = voxel_sh1.getConstantLocation("envmapStrength"); + #if arm_envldr + voxel_cf1 = voxel_sh1.getConstantLocation("backgroundCol"); + #end + voxel_cg1 = voxel_sh1.getConstantLocation("eye"); + #else + #if arm_voxelgi_shadows + voxel_tf1 = voxel_sh1.getTextureUnit("SDF"); + #end + #end + } + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + if (voxel_sh2 == null) + { + voxel_sh2 = path.getComputeShader("voxel_sdf_jumpflood"); + voxel_ta2 = voxel_sh2.getTextureUnit("voxelsSDF"); + voxel_tb2 = voxel_sh2.getTextureUnit("voxelsSDFtmp"); + + voxel_ca2 = voxel_sh2.getConstantLocation("clipmaps"); + voxel_cb2 = voxel_sh2.getConstantLocation("clipmapLevel"); + voxel_cc2 = voxel_sh2.getConstantLocation("jump_size"); + } + #end + #if arm_deferred + if (voxel_sh3 == null) + { + #if (rp_voxels == "Voxel AO") + voxel_sh3 = path.getComputeShader("voxel_resolve_ao"); + #else + voxel_sh3 = path.getComputeShader("voxel_resolve_diffuse"); + #end + voxel_ta3 = voxel_sh3.getTextureUnit("voxels"); + voxel_tb3 = voxel_sh3.getTextureUnit("gbufferD"); + voxel_tc3 = voxel_sh3.getTextureUnit("gbuffer0"); + #if (rp_voxels == "Voxel AO") + voxel_td3 = voxel_sh3.getTextureUnit("voxels_ao"); + #else + voxel_td3 = voxel_sh3.getTextureUnit("voxels_diffuse"); + #end + voxel_ca3 = voxel_sh3.getConstantLocation("clipmaps"); + voxel_cb3 = voxel_sh3.getConstantLocation("InvVP"); + voxel_cc3 = voxel_sh3.getConstantLocation("cameraProj"); + voxel_cd3 = voxel_sh3.getConstantLocation("eye"); + voxel_ce3 = voxel_sh3.getConstantLocation("eyeLook"); + voxel_cf3 = voxel_sh3.getConstantLocation("postprocess_resolution"); + } + #if (rp_voxels == "Voxel GI") + if (voxel_sh4 == null) + { + voxel_sh4 = path.getComputeShader("voxel_resolve_specular"); + voxel_ta4 = voxel_sh4.getTextureUnit("voxels"); + voxel_tb4 = voxel_sh4.getTextureUnit("gbufferD"); + voxel_tc4 = voxel_sh4.getTextureUnit("gbuffer0"); + voxel_td4 = voxel_sh4.getTextureUnit("voxelsSDF"); + voxel_te4 = voxel_sh4.getTextureUnit("voxels_specular"); + + voxel_ca4 = voxel_sh4.getConstantLocation("clipmaps"); + voxel_cb4 = voxel_sh4.getConstantLocation("InvVP"); + voxel_cc4 = voxel_sh4.getConstantLocation("cameraProj"); + voxel_cd4 = voxel_sh4.getConstantLocation("eye"); + voxel_ce4 = voxel_sh4.getConstantLocation("eyeLook"); + voxel_cf4 = voxel_sh4.getConstantLocation("postprocess_resolution"); + } + #end + #end // arm_deferred + #if (rp_voxels == "Voxel GI") + if (voxel_sh5 == null) { + voxel_sh5 = path.getComputeShader("voxel_light"); + voxel_ta5 = voxel_sh5.getTextureUnit("voxelsLight"); + + voxel_ca5 = voxel_sh5.getConstantLocation("clipmaps"); + voxel_cb5 = voxel_sh5.getConstantLocation("clipmapLevel"); + + voxel_cc5 = voxel_sh5.getConstantLocation("lightPos"); + voxel_cd5 = voxel_sh5.getConstantLocation("lightColor"); + voxel_ce5 = voxel_sh5.getConstantLocation("lightType"); + voxel_cf5 = voxel_sh5.getConstantLocation("lightDir"); + voxel_cg5 = voxel_sh5.getConstantLocation("spotData"); + #if rp_shadowmap + voxel_tb5 = voxel_sh5.getTextureUnit("shadowMap"); + voxel_tc5 = voxel_sh5.getTextureUnit("shadowMapSpot"); + voxel_td5 = voxel_sh5.getTextureUnit("shadowMapPoint"); + + voxel_ch5 = voxel_sh5.getConstantLocation("lightShadow"); + voxel_ci5 = voxel_sh5.getConstantLocation("lightProj"); + voxel_cj5 = voxel_sh5.getConstantLocation("LVP"); + voxel_ck5 = voxel_sh5.getConstantLocation("shadowsBias"); + #if arm_shadowmap_atlas + voxel_cl5 = voxel_sh5.getConstantLocation("index"); + voxel_cm5 = voxel_sh5.getConstantLocation("pointLightDataArray"); + #end + #end + } + #end + } + + public static function computeVoxelsOffsetPrev() { + var rts = path.renderTargets; + var res = iron.RenderPath.getVoxelRes(); + var clipmaps = iron.RenderPath.clipmaps; + var clipmap = clipmaps[iron.RenderPath.clipmapLevel]; + + kha.compute.Compute.setShader(voxel_sh0); + + kha.compute.Compute.setTexture(voxel_ta0, rts.get("voxelsOut").image, kha.compute.Access.Read); + kha.compute.Compute.setTexture(voxel_tb0, rts.get("voxelsOutB").image, kha.compute.Access.Write); + + var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10); + for (i in 0...Main.voxelgiClipmapCount) { + fa[i * 10] = clipmaps[i].voxelSize; + fa[i * 10 + 1] = clipmaps[i].extents.x; + fa[i * 10 + 2] = clipmaps[i].extents.y; + fa[i * 10 + 3] = clipmaps[i].extents.z; + fa[i * 10 + 4] = clipmaps[i].center.x; + fa[i * 10 + 5] = clipmaps[i].center.y; + fa[i * 10 + 6] = clipmaps[i].center.z; + fa[i * 10 + 7] = clipmaps[i].offset_prev.x; + fa[i * 10 + 8] = clipmaps[i].offset_prev.y; + fa[i * 10 + 9] = clipmaps[i].offset_prev.z; + } + + kha.compute.Compute.setFloats(voxel_ca0, fa); + + kha.compute.Compute.setInt(voxel_cb0, iron.RenderPath.clipmapLevel); + + kha.compute.Compute.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8)); + } + + public static function computeVoxelsTemporal() { + var rts = path.renderTargets; + var res = iron.RenderPath.getVoxelRes(); + var camera = iron.Scene.active.camera; + var clipmaps = iron.RenderPath.clipmaps; + var clipmap = clipmaps[iron.RenderPath.clipmapLevel]; + + kha.compute.Compute.setShader(voxel_sh1); + + kha.compute.Compute.setTexture(voxel_ta1, rts.get("voxels").image, kha.compute.Access.Read); + kha.compute.Compute.setTexture(voxel_tb1, rts.get("voxelsOutB").image, kha.compute.Access.Read); + kha.compute.Compute.setTexture(voxel_tc1, rts.get("voxelsOut").image, kha.compute.Access.Write); + #if (rp_voxels == "Voxel GI") + kha.compute.Compute.setSampledTexture(voxel_td1, rts.get("voxelsOutB").image); + kha.compute.Compute.setTexture(voxel_te1, rts.get("voxelsLight").image, kha.compute.Access.Read); + kha.compute.Compute.setTexture(voxel_tf1, rts.get("voxelsSDF").image, kha.compute.Access.Write); + #else + #if arm_voxelgi_shadows + kha.compute.Compute.setTexture(voxel_tf1, rts.get("voxelsSDF").image, kha.compute.Access.Write); + #end + #end + + var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10); + for (i in 0...Main.voxelgiClipmapCount) { + fa[i * 10] = clipmaps[i].voxelSize; + fa[i * 10 + 1] = clipmaps[i].extents.x; + fa[i * 10 + 2] = clipmaps[i].extents.y; + fa[i * 10 + 3] = clipmaps[i].extents.z; + fa[i * 10 + 4] = clipmaps[i].center.x; + fa[i * 10 + 5] = clipmaps[i].center.y; + fa[i * 10 + 6] = clipmaps[i].center.z; + fa[i * 10 + 7] = clipmaps[i].offset_prev.x; + fa[i * 10 + 8] = clipmaps[i].offset_prev.y; + fa[i * 10 + 9] = clipmaps[i].offset_prev.z; + } + + kha.compute.Compute.setFloats(voxel_ca1, fa); + + kha.compute.Compute.setInt(voxel_cb1, iron.RenderPath.clipmapLevel); + + #if (rp_voxels == "Voxel GI" && arm_deferred) + kha.compute.Compute.setSampledTexture(voxel_tg1, rts.get("gbuffer0").image); + kha.compute.Compute.setSampledTexture(voxel_th1, rts.get("gbuffer1").image); + #if rp_gbuffer2 + kha.compute.Compute.setSampledTexture(voxel_ti1, rts.get("gbuffer2").image); + #end + #if arm_brdf + kha.compute.Compute.setSampledTexture(voxel_tj1, iron.Scene.active.embedded.get("brdf.png")); + #end + #if arm_radiance + kha.compute.Compute.setSampledTexture(voxel_tk1, iron.Scene.active.world.probe.radiance); + var w = iron.Scene.active.world; + var i = w != null ? w.probe.raw.radiance_mipmaps + 1 - 2 : 1; + kha.compute.Compute.setFloat(voxel_ce1, i); + #end + #if arm_irradiance + fa = iron.Scene.active.world == null ? iron.data.WorldData.getEmptyIrradiance() : iron.Scene.active.world.probe.irradiance; + kha.compute.Compute.setFloats(voxel_cc1, fa); + #end + kha.compute.Compute.setFloat(voxel_cd1, iron.Scene.active.world == null ? 0.0 : iron.Scene.active.world.probe.raw.strength); + + #if arm_envldr + var envCol:iron.math.Vec3; + if (camera.data.raw.clear_color != null) + envCol = new iron.math.Vec3(camera.data.raw.clear_color[0], camera.data.raw.clear_color[1], camera.data.raw.clear_color[2]); + else + envCol = new iron.math.Vec3(0.0); + kha.compute.Compute.setFloat3(voxel_cf1, envCol.x, envCol.y, envCol.z); + #end + kha.compute.Compute.setFloat3(voxel_cg1, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz()); + #end + + kha.compute.Compute.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8)); + } + + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + public static function computeVoxelsSDF() { + var rts = path.renderTargets; + var res = iron.RenderPath.getVoxelRes(); + var clipmaps = iron.RenderPath.clipmaps; + var clipmap = clipmaps[iron.RenderPath.clipmapLevel]; + + var read_sdf = "voxelsSDF"; + var write_sdf = "voxelsSDFtmp"; + + var passcount = Std.int(Math.ceil(Math.log(res) / Math.log(2.0))); + + for (i in 0...passcount) { + kha.compute.Compute.setShader(voxel_sh2); + + kha.compute.Compute.setTexture(voxel_ta2, rts.get(read_sdf).image, kha.compute.Access.Read); + kha.compute.Compute.setTexture(voxel_tb2, rts.get(write_sdf).image, kha.compute.Access.Write); + + var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10); + for (i in 0...Main.voxelgiClipmapCount) { + fa[i * 10] = clipmaps[i].voxelSize; + fa[i * 10 + 1] = clipmaps[i].extents.x; + fa[i * 10 + 2] = clipmaps[i].extents.y; + fa[i * 10 + 3] = clipmaps[i].extents.z; + fa[i * 10 + 4] = clipmaps[i].center.x; + fa[i * 10 + 5] = clipmaps[i].center.y; + fa[i * 10 + 6] = clipmaps[i].center.z; + fa[i * 10 + 7] = clipmaps[i].offset_prev.x; + fa[i * 10 + 8] = clipmaps[i].offset_prev.y; + fa[i * 10 + 9] = clipmaps[i].offset_prev.z; + } + + kha.compute.Compute.setFloats(voxel_ca2, fa); + + kha.compute.Compute.setInt(voxel_cb2, iron.RenderPath.clipmapLevel); + + var jump_size = Math.pow(2.0, passcount - i - 1); + kha.compute.Compute.setFloat(voxel_cc2, jump_size); + + kha.compute.Compute.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8)); + + if (i < passcount - 1) + { + read_sdf = read_sdf == "voxelsSDF" ? "voxelsSDFtmp" : "voxelsSDF"; + write_sdf = write_sdf == "voxelsSDF" ? "voxelsSDFtmp" : "voxelsSDF"; + } + } + } + #end + + #if arm_deferred + #if (rp_voxels == "Voxel AO") + public static function resolveAO() { + var rts = path.renderTargets; + var res = iron.RenderPath.getVoxelRes(); + var camera = iron.Scene.active.camera; + var clipmaps = iron.RenderPath.clipmaps; + var clipmap = clipmaps[iron.RenderPath.clipmapLevel]; + + kha.compute.Compute.setShader(voxel_sh3); + + kha.compute.Compute.setSampledTexture(voxel_ta3, rts.get("voxelsOut").image); + kha.compute.Compute.setSampledTexture(voxel_tb3, rts.get("half").image); + kha.compute.Compute.setSampledTexture(voxel_tc3, rts.get("gbuffer0").image); + kha.compute.Compute.setTexture(voxel_td3, rts.get("voxels_ao").image, kha.compute.Access.Write); + + var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10); + for (i in 0...Main.voxelgiClipmapCount) { + fa[i * 10] = clipmaps[i].voxelSize; + fa[i * 10 + 1] = clipmaps[i].extents.x; + fa[i * 10 + 2] = clipmaps[i].extents.y; + fa[i * 10 + 3] = clipmaps[i].extents.z; + fa[i * 10 + 4] = clipmaps[i].center.x; + fa[i * 10 + 5] = clipmaps[i].center.y; + fa[i * 10 + 6] = clipmaps[i].center.z; + fa[i * 10 + 7] = clipmaps[i].offset_prev.x; + fa[i * 10 + 8] = clipmaps[i].offset_prev.y; + fa[i * 10 + 9] = clipmaps[i].offset_prev.z; + } + + kha.compute.Compute.setFloats(voxel_ca3, fa); + + #if arm_centerworld + m.setFrom(vmat(camera.V)); + #else + m.setFrom(camera.V); + #end + m.multmat(camera.P); + m.getInverse(m); + + kha.compute.Compute.setMatrix(voxel_cb3, m.self); + + var near = camera.data.raw.near_plane; + var far = camera.data.raw.far_plane; + var v = new iron.math.Vec2(); + v.x = far / (far - near); + v.y = (-far * near) / (far - near); + + kha.compute.Compute.setFloat2(voxel_cc3, v.x, v.y); + + + kha.compute.Compute.setFloat3(voxel_cd3, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz()); + var eyeLook = camera.lookWorld().normalize(); + kha.compute.Compute.setFloat3(voxel_ce3, eyeLook.x, eyeLook.y, eyeLook.z); + + var width = iron.App.w(); + var height = iron.App.h(); + var dp = getDisplayp(); + if (dp != null) { // 1080p/.. + if (width > height) { + width = Std.int(width * (dp / height)); + height = dp; + } + else { + height = Std.int(height * (dp / width)); + width = dp; + } + } + kha.compute.Compute.setFloat2(voxel_cf3, width, height); + + kha.compute.Compute.compute(Std.int(width / 8), Std.int(height / 8), 1); + } + + #else + public static function resolveDiffuse() { + var rts = path.renderTargets; + var res = iron.RenderPath.getVoxelRes(); + var camera = iron.Scene.active.camera; + var clipmaps = iron.RenderPath.clipmaps; + var clipmap = clipmaps[iron.RenderPath.clipmapLevel]; + + kha.compute.Compute.setShader(voxel_sh3); + + kha.compute.Compute.setSampledTexture(voxel_ta3, rts.get("voxelsOut").image); + kha.compute.Compute.setSampledTexture(voxel_tb3, rts.get("half").image); + kha.compute.Compute.setSampledTexture(voxel_tc3, rts.get("gbuffer0").image); + kha.compute.Compute.setTexture(voxel_td3, rts.get("voxels_diffuse").image, kha.compute.Access.Write); + + var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10); + for (i in 0...Main.voxelgiClipmapCount) { + fa[i * 10] = clipmaps[i].voxelSize; + fa[i * 10 + 1] = clipmaps[i].extents.x; + fa[i * 10 + 2] = clipmaps[i].extents.y; + fa[i * 10 + 3] = clipmaps[i].extents.z; + fa[i * 10 + 4] = clipmaps[i].center.x; + fa[i * 10 + 5] = clipmaps[i].center.y; + fa[i * 10 + 6] = clipmaps[i].center.z; + fa[i * 10 + 7] = clipmaps[i].offset_prev.x; + fa[i * 10 + 8] = clipmaps[i].offset_prev.y; + fa[i * 10 + 9] = clipmaps[i].offset_prev.z; + } + + kha.compute.Compute.setFloats(voxel_ca3, fa); + + #if arm_centerworld + m.setFrom(vmat(camera.V)); + #else + m.setFrom(camera.V); + #end + m.multmat(camera.P); + m.getInverse(m); + + kha.compute.Compute.setMatrix(voxel_cb3, m.self); + + var near = camera.data.raw.near_plane; + var far = camera.data.raw.far_plane; + var v = new iron.math.Vec2(); + v.x = far / (far - near); + v.y = (-far * near) / (far - near); + + kha.compute.Compute.setFloat2(voxel_cc3, v.x, v.y); + + + kha.compute.Compute.setFloat3(voxel_cd3, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz()); + var eyeLook = camera.lookWorld().normalize(); + kha.compute.Compute.setFloat3(voxel_ce3, eyeLook.x, eyeLook.y, eyeLook.z); + + var width = iron.App.w(); + var height = iron.App.h(); + var dp = getDisplayp(); + if (dp != null) { // 1080p/.. + if (width > height) { + width = Std.int(width * (dp / height)); + height = dp; + } + else { + height = Std.int(height * (dp / width)); + width = dp; + } + } + kha.compute.Compute.setFloat2(voxel_cf3, width, height); + + kha.compute.Compute.compute(Std.int(width / 8), Std.int(height / 8), 1); + } + + public static function resolveSpecular() { + var rts = path.renderTargets; + var res = iron.RenderPath.getVoxelRes(); + var camera = iron.Scene.active.camera; + var clipmaps = iron.RenderPath.clipmaps; + var clipmap = clipmaps[iron.RenderPath.clipmapLevel]; + + kha.compute.Compute.setShader(voxel_sh4); + + kha.compute.Compute.setSampledTexture(voxel_ta4, rts.get("voxelsOut").image); + kha.compute.Compute.setSampledTexture(voxel_tb4, rts.get("half").image); + kha.compute.Compute.setSampledTexture(voxel_tc4, rts.get("gbuffer0").image); + kha.compute.Compute.setSampledTexture(voxel_td4, rts.get("voxelsSDF").image); + kha.compute.Compute.setTexture(voxel_te4, rts.get("voxels_specular").image, kha.compute.Access.Write); + + var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10); + for (i in 0...Main.voxelgiClipmapCount) { + fa[i * 10] = clipmaps[i].voxelSize; + fa[i * 10 + 1] = clipmaps[i].extents.x; + fa[i * 10 + 2] = clipmaps[i].extents.y; + fa[i * 10 + 3] = clipmaps[i].extents.z; + fa[i * 10 + 4] = clipmaps[i].center.x; + fa[i * 10 + 5] = clipmaps[i].center.y; + fa[i * 10 + 6] = clipmaps[i].center.z; + fa[i * 10 + 7] = clipmaps[i].offset_prev.x; + fa[i * 10 + 8] = clipmaps[i].offset_prev.y; + fa[i * 10 + 9] = clipmaps[i].offset_prev.z; + } + + kha.compute.Compute.setFloats(voxel_ca4, fa); + + #if arm_centerworld + m.setFrom(vmat(camera.V)); + #else + m.setFrom(camera.V); + #end + m.multmat(camera.P); + m.getInverse(m); + + kha.compute.Compute.setMatrix(voxel_cb4, m.self); + + var near = camera.data.raw.near_plane; + var far = camera.data.raw.far_plane; + var v = new iron.math.Vec2(); + v.x = far / (far - near); + v.y = (-far * near) / (far - near); + + kha.compute.Compute.setFloat2(voxel_cc4, v.x, v.y); + + + kha.compute.Compute.setFloat3(voxel_cd4, camera.transform.worldx(), camera.transform.worldy(), camera.transform.worldz()); + var eyeLook = camera.lookWorld().normalize(); + kha.compute.Compute.setFloat3(voxel_ce4, eyeLook.x, eyeLook.y, eyeLook.z); + + var width = iron.App.w(); + var height = iron.App.h(); + var dp = getDisplayp(); + if (dp != null) { // 1080p/.. + if (width > height) { + width = Std.int(width * (dp / height)); + height = dp; + } + else { + height = Std.int(height * (dp / width)); + width = dp; + } + } + kha.compute.Compute.setFloat2(voxel_cf4, width, height); + + kha.compute.Compute.compute(Std.int(width / 8), Std.int(height / 8), 1); + } + #end // voxel ao + #end // deferred + + #if (rp_voxels == "Voxel GI") + public static function computeVoxelsLight() { + var rts = path.renderTargets; + var res = iron.RenderPath.getVoxelRes(); + var camera = iron.Scene.active.camera; + var clipmaps = iron.RenderPath.clipmaps; + var clipmap = clipmaps[iron.RenderPath.clipmapLevel]; + var lights = iron.Scene.active.lights; + + pointIndex = spotIndex = 0; + for (i in 0...lights.length) { + var l = lights[i]; + if (!l.visible) continue; + path.light = l; + + kha.compute.Compute.setShader(voxel_sh5); + + kha.compute.Compute.setTexture(voxel_ta5, rts.get("voxelsLight").image, kha.compute.Access.Write); + + var fa:Float32Array = new Float32Array(Main.voxelgiClipmapCount * 10); + for (i in 0...Main.voxelgiClipmapCount) { + fa[i * 10] = clipmaps[i].voxelSize; + fa[i * 10 + 1] = clipmaps[i].extents.x; + fa[i * 10 + 2] = clipmaps[i].extents.y; + fa[i * 10 + 3] = clipmaps[i].extents.z; + fa[i * 10 + 4] = clipmaps[i].center.x; + fa[i * 10 + 5] = clipmaps[i].center.y; + fa[i * 10 + 6] = clipmaps[i].center.z; + fa[i * 10 + 7] = clipmaps[i].offset_prev.x; + fa[i * 10 + 8] = clipmaps[i].offset_prev.y; + fa[i * 10 + 9] = clipmaps[i].offset_prev.z; + } + + kha.compute.Compute.setFloats(voxel_ca5, fa); + + kha.compute.Compute.setInt(voxel_cb5, iron.RenderPath.clipmapLevel); + + #if rp_shadowmap + if (l.data.raw.type == "sun") { + #if arm_shadowmap_atlas + kha.compute.Compute.setSampledTexture(voxel_tb5, rts.get("shadowMapAtlasSun").image); + #else + kha.compute.Compute.setSampledTexture(voxel_tb5, rts.get("shadowMap").image); + #end + kha.compute.Compute.setInt(voxel_ch5, 1); // lightShadow + } + else if (l.data.raw.type == "spot" || l.data.raw.type == "area") { + #if arm_shadowmap_atlas + kha.compute.Compute.setSampledTexture(voxel_tc5, rts.get("shadowMapAtlasSpot").image); + #else + kha.compute.Compute.setSampledTexture(voxel_tc5, rts.get("shadowMapSpot[" + spotIndex + "]").image); + spotIndex++; + #end + kha.compute.Compute.setInt(voxel_ch5, 2); + } + else { + #if arm_shadowmap_atlas + kha.compute.Compute.setSampledTexture(voxel_td5, rts.get("shadowMapAtlasPoint").image); + kha.compute.Compute.setInt(voxel_cl5, i); + kha.compute.Compute.setFloats(voxel_cm5, iron.object.LightObject.pointLightsData); + #else + kha.compute.Compute.setSampledCubeMap(voxel_td5, rts.get("shadowMapPoint[" + pointIndex + "]").cubeMap); + pointIndex++; + #end + kha.compute.Compute.setInt(voxel_ch5, 3); + } + + // lightProj + var near = l.data.raw.near_plane; + var far = l.data.raw.far_plane; + var a:kha.FastFloat = far + near; + var b:kha.FastFloat = far - near; + var f2:kha.FastFloat = 2.0; + var c:kha.FastFloat = f2 * far * near; + var vx:kha.FastFloat = a / b; + var vy:kha.FastFloat = c / b; + kha.compute.Compute.setFloat2(voxel_ci5, vx, vy); + // LVP + m.setFrom(l.VP); + m.multmat(iron.object.Uniforms.biasMat); + /* + #if arm_shadowmap_atlas + if (l.data.raw.type == "sun") + { + // tile matrix + m.setIdentity(); + // scale [0-1] coords to [0-tilescale] + m2._00 = l.tileScale[0]; + m2._11 = l.tileScale[0]; + // offset coordinate start from [0, 0] to [tile-start-x, tile-start-y] + m2._30 = l.tileOffsetX[0]; + m2._31 = l.tileOffsetY[0]; + m.multmat(m2); + #if (!kha_opengl) + m2.setIdentity(); + m2._11 = -1.0; + m2._31 = 1.0; + m.multmat(m2); + #end + } + #end + */ + kha.compute.Compute.setMatrix(voxel_cj5, m.self); + // shadowsBias + kha.compute.Compute.setFloat(voxel_ck5, l.data.raw.shadows_bias); + #end // rp_shadowmap + + // lightPos + kha.compute.Compute.setFloat3(voxel_cc5, l.transform.worldx(), l.transform.worldy(), l.transform.worldz()); + // lightCol + var f = l.data.raw.strength; + kha.compute.Compute.setFloat3(voxel_cd5, l.data.raw.color[0] * f, l.data.raw.color[1] * f, l.data.raw.color[2] * f); + // lightType + kha.compute.Compute.setInt(voxel_ce5, iron.data.LightData.typeToInt(l.data.raw.type)); + // lightDir + var v = l.look(); + kha.compute.Compute.setFloat3(voxel_cf5, v.x, v.y, v.z); + // spotData + if (l.data.raw.type == "spot") { + var vx = l.data.raw.spot_size; + var vy = vx - l.data.raw.spot_blend; + kha.compute.Compute.setFloat2(voxel_cg5, vx, vy); + } + + kha.compute.Compute.compute(Std.int(res / 8), Std.int(res / 8), Std.int(res / 8)); + } + } + #end // GI + #end // Voxels } #if arm_shadowmap_atlas diff --git a/Sources/armory/renderpath/RenderPathCreator.hx b/Sources/armory/renderpath/RenderPathCreator.hx index 4662117e48..33cf30973c 100644 --- a/Sources/armory/renderpath/RenderPathCreator.hx +++ b/Sources/armory/renderpath/RenderPathCreator.hx @@ -57,11 +57,6 @@ class RenderPathCreator { return path; } - #if rp_voxels - public static var voxelFrame = 0; - public static var voxelFreq = 6; // Revoxelizing frequency - #end - // Last target before drawing to framebuffer public static var finalTarget: RenderTarget = null; } diff --git a/Sources/armory/renderpath/RenderPathDeferred.hx b/Sources/armory/renderpath/RenderPathDeferred.hx index e7f14aec20..9dd603afbd 100644 --- a/Sources/armory/renderpath/RenderPathDeferred.hx +++ b/Sources/armory/renderpath/RenderPathDeferred.hx @@ -2,6 +2,7 @@ package armory.renderpath; import iron.RenderPath; import iron.Scene; +import iron.object.Clipmap; class RenderPathDeferred { @@ -9,11 +10,6 @@ class RenderPathDeferred { static var path: RenderPath; - #if rp_voxels - static var voxels = "voxels"; - static var voxelsLast = "voxels"; - #end - #if rp_bloom static var bloomDownsampler: Downsampler; static var bloomUpsampler: Upsampler; @@ -50,23 +46,38 @@ class RenderPathDeferred { } #end - #if (rp_translucency) + #if (rp_translucency && !rp_ssrefr) { Inc.initTranslucency(); } #end - #if rp_voxels + #if (rp_voxels != 'Off') { - Inc.initGI(); - #if arm_voxelgi_temporal - { - Inc.initGI("voxelsB"); - } + Inc.initGI("voxels"); + Inc.initGI("voxelsOut"); + Inc.initGI("voxelsOutB"); + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + Inc.initGI("voxelsSDF"); + Inc.initGI("voxelsSDFtmp"); #end - #if rp_voxels + #if (rp_voxels == "Voxel GI") + Inc.initGI("voxelsLight"); + Inc.initGI("voxels_diffuse"); + Inc.initGI("voxels_specular"); + #else path.loadShader("shader_datas/deferred_light/deferred_light_VoxelAOvar"); + Inc.initGI("voxels_ao"); #end + iron.RenderPath.clipmaps = new Array(); + for (i in 0...Main.voxelgiClipmapCount) { + var clipmap = new iron.object.Clipmap(); + clipmap.voxelSize = Main.voxelgiVoxelSize * Math.pow(2.0, i); + clipmap.extents = new iron.math.Vec3(0.0); + clipmap.center = new iron.math.Vec3(0.0); + clipmap.offset_prev = new iron.math.Vec3(0.0); + iron.RenderPath.clipmaps.push(clipmap); + } } #end @@ -316,7 +327,7 @@ class RenderPathDeferred { } #end - #if (rp_ssr_half || rp_ssgi_half) + #if (rp_ssr_half || rp_ssgi_half || rp_voxels != "Off") { path.loadShader("shader_datas/downsample_depth/downsample_depth"); var t = new RenderTargetRaw(); @@ -491,7 +502,7 @@ class RenderPathDeferred { } #end - #if (rp_ssr_half || rp_ssgi_half) + #if (rp_ssr_half || rp_ssgi_half || rp_voxels != "Off") path.setTarget("half"); path.bindTarget("_main", "texdepth"); path.drawShader("shader_datas/downsample_depth/downsample_depth"); @@ -551,33 +562,60 @@ class RenderPathDeferred { #end // Voxels - #if rp_voxels + #if (rp_voxels != 'Off') if (armory.data.Config.raw.rp_gi != false) { - var voxelize = path.voxelize(); + var path = RenderPath.active; - #if arm_voxelgi_temporal - voxelize = ++RenderPathCreator.voxelFrame % RenderPathCreator.voxelFreq == 0; + Inc.computeVoxelsBegin(); - if (voxelize) { - voxels = voxels == "voxels" ? "voxelsB" : "voxels"; - voxelsLast = voxels == "voxels" ? "voxelsB" : "voxels"; + if (iron.RenderPath.pre_clear == true) + { + #if (rp_voxels == "Voxel GI") + path.clearImage("voxelsLight", 0x00000000); + #end + path.clearImage("voxels", 0x00000000); + path.clearImage("voxelsOut", 0x00000000); + path.clearImage("voxelsOutB", 0x00000000); + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + path.clearImage("voxelsSDF", 0x00000000); + path.clearImage("voxelsSDFtmp", 0x00000000); + #end + iron.RenderPath.pre_clear = false; } + else + { + path.clearImage("voxels", 0x00000000); + Inc.computeVoxelsOffsetPrev(); + } + + path.setTarget(""); + var res = iron.RenderPath.getVoxelRes(); + path.setViewport(res, res); + + path.bindTarget("voxels", "voxels"); + path.drawMeshes("voxel"); + #if (rp_voxels == "Voxel GI") + Inc.computeVoxelsLight(); #end + Inc.computeVoxelsTemporal(); - if (voxelize) { - var res = Inc.getVoxelRes(); - var voxtex = voxels; + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + Inc.computeVoxelsSDF(); + #end - path.clearImage(voxtex, 0x00000000); - path.setTarget(""); - path.setViewport(res, res); - path.bindTarget(voxtex, "voxels"); - path.drawMeshes("voxel"); - path.generateMipmaps(voxels); + if (iron.RenderPath.res_pre_clear == true) { + iron.RenderPath.res_pre_clear = false; + #if (rp_voxels == "Voxel GI") + path.clearImage("voxels_diffuse", 0x00000000); + path.clearImage("voxels_specular", 0x00000000); + #else + path.clearImage("voxels_ao", 0x00000000); + #end } } #end + // --- // Deferred light // --- @@ -588,7 +626,7 @@ class RenderPathDeferred { path.bindTarget("_main", "gbufferD"); path.bindTarget("gbuffer0", "gbuffer0"); path.bindTarget("gbuffer1", "gbuffer1"); - + #if rp_gbuffer2 { path.bindTarget("gbuffer2", "gbuffer2"); @@ -613,17 +651,24 @@ class RenderPathDeferred { #end var voxelao_pass = false; - #if rp_voxels + #if (rp_voxels != "Off") if (armory.data.Config.raw.rp_gi != false) { - #if arm_config + #if (arm_config && (rp_voxels == "Voxel AO")) voxelao_pass = true; #end - path.bindTarget(voxels, "voxels"); - #if arm_voxelgi_temporal - { - path.bindTarget(voxelsLast, "voxelsLast"); - } + #if (rp_voxels == "Voxel AO") + Inc.resolveAO(); + path.bindTarget("voxels_ao", "voxels_ao"); + #else + Inc.resolveDiffuse(); + Inc.resolveSpecular(); + path.bindTarget("voxels_diffuse", "voxels_diffuse"); + path.bindTarget("voxels_specular", "voxels_specular"); + #end + #if arm_voxelgi_shadows + path.bindTarget("voxelsOut", "voxels"); + path.bindTarget("voxelsSDF", "voxelsSDF"); #end } #end @@ -702,7 +747,7 @@ class RenderPathDeferred { } #end - #if rp_translucency + #if (rp_translucency && !rp_ssrefr) { Inc.drawTranslucency("tex"); } @@ -774,6 +819,12 @@ class RenderPathDeferred { path.drawShader("shader_datas/copy_pass/copy_pass"); path.setTarget("tex", ["gbuffer0", "gbuffer_refraction"]); + #if (rp_voxels != "Off") + path.bindTarget("voxelsOut", "voxels"); + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + path.bindTarget("voxelsSDF", "voxelsSDF"); + #end + #end path.drawMeshes("refraction"); path.setTarget("tex"); diff --git a/Sources/armory/renderpath/RenderPathForward.hx b/Sources/armory/renderpath/RenderPathForward.hx index 0df28f28cd..87dea914f3 100644 --- a/Sources/armory/renderpath/RenderPathForward.hx +++ b/Sources/armory/renderpath/RenderPathForward.hx @@ -2,6 +2,7 @@ package armory.renderpath; import iron.RenderPath; import iron.Scene; +import iron.object.Clipmap; class RenderPathForward { @@ -9,9 +10,8 @@ class RenderPathForward { static var path: RenderPath; - #if rp_voxels - static var voxels = "voxels"; - static var voxelsLast = "voxels"; + #if (rp_voxels != "Off") + static var res_pre_clear = true; #end #if rp_bloom @@ -53,7 +53,7 @@ class RenderPathForward { } #end - #if rp_translucency + #if (rp_translucency && !rp_ssrefr) { RenderPathCreator.setTargetMeshes(); Inc.drawTranslucency("lbuffer0"); @@ -91,7 +91,7 @@ class RenderPathForward { } #end - #if rp_render_to_texture + #if (rp_render_to_texture) { path.createDepthBuffer("main", "DEPTH24"); @@ -185,20 +185,33 @@ class RenderPathForward { } #end - #if (rp_translucency) + #if (rp_translucency && !rp_ssrefr) { Inc.initTranslucency(); } #end - #if rp_voxels + #if (rp_voxels != "Off") { - Inc.initGI(); - #if arm_voxelgi_temporal - { - Inc.initGI("voxelsB"); - } + Inc.initGI("voxels"); + Inc.initGI("voxelsOut"); + Inc.initGI("voxelsOutB"); + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + Inc.initGI("voxelsSDF"); + Inc.initGI("voxelsSDFtmp"); #end + #if (rp_voxels == "Voxel GI") + Inc.initGI("voxelsLight"); + #end + iron.RenderPath.clipmaps = new Array(); + for (i in 0...Main.voxelgiClipmapCount) { + var clipmap = new iron.object.Clipmap(); + clipmap.voxelSize = Main.voxelgiVoxelSize * Math.pow(2.0, i); + clipmap.extents = new iron.math.Vec3(0.0); + clipmap.center = new iron.math.Vec3(0.0); + clipmap.offset_prev = new iron.math.Vec3(0.0); + iron.RenderPath.clipmaps.push(clipmap); + } } #end @@ -350,30 +363,50 @@ class RenderPathForward { } #end - #if rp_voxels + // Voxels + #if (rp_voxels != 'Off') + if (armory.data.Config.raw.rp_gi != false) { - var voxelize = path.voxelize(); + var path = RenderPath.active; - #if arm_voxelgi_temporal - voxelize = ++RenderPathCreator.voxelFrame % RenderPathCreator.voxelFreq == 0; + Inc.computeVoxelsBegin(); - if (voxelize) { - voxels = voxels == "voxels" ? "voxelsB" : "voxels"; - voxelsLast = voxels == "voxels" ? "voxelsB" : "voxels"; + if (iron.RenderPath.pre_clear == true) + { + #if (rp_voxels == "Voxel GI") + path.clearImage("voxelsLight", 0x00000000); + #end + path.clearImage("voxels", 0x00000000); + path.clearImage("voxelsOut", 0x00000000); + path.clearImage("voxelsOutB", 0x00000000); + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + path.clearImage("voxelsSDF", 0x00000000); + path.clearImage("voxelsSDFtmp", 0x00000000); + #end + iron.RenderPath.pre_clear = false; + } + else + { + path.clearImage("voxels", 0x00000000); + Inc.computeVoxelsOffsetPrev(); } + + path.setTarget(""); + var res = iron.RenderPath.getVoxelRes(); + path.setViewport(res, res); + + path.bindTarget("voxels", "voxels"); + path.drawMeshes("voxel"); + + #if (rp_voxels == "Voxel GI") + Inc.computeVoxelsLight(); #end + Inc.computeVoxelsTemporal(); - if (voxelize) { - var res = Inc.getVoxelRes(); - var voxtex = voxels; + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + Inc.computeVoxelsSDF(); + #end - path.clearImage(voxtex, 0x00000000); - path.setTarget(""); - path.setViewport(res, res); - path.bindTarget(voxtex, "voxels"); - path.drawMeshes("voxel"); - path.generateMipmaps(voxels); - } } #end @@ -406,13 +439,13 @@ class RenderPathForward { } #end - #if rp_voxels + + #if (rp_voxels != "Off") + if (armory.data.Config.raw.rp_gi != false) { - path.bindTarget(voxels, "voxels"); - #if arm_voxelgi_temporal - { - path.bindTarget(voxelsLast, "voxelsLast"); - } + path.bindTarget("voxelsOut", "voxels"); + #if (arm_voxelgi_shadows || rp_voxels == "Voxel GI") + path.bindTarget("voxelsSDF", "voxelsSDF"); #end } #end diff --git a/blender/arm/lightmapper/properties/renderer/cycles.py b/blender/arm/lightmapper/properties/renderer/cycles.py index f4ac212abb..e8838587fe 100644 --- a/blender/arm/lightmapper/properties/renderer/cycles.py +++ b/blender/arm/lightmapper/properties/renderer/cycles.py @@ -112,4 +112,4 @@ class TLM_CyclesSceneProperties(bpy.types.PropertyGroup): tlm_premultiply_ao : BoolProperty( name="Premultiply AO", description="Ambient Occlusion will be premultiplied together with lightmaps, requiring less textures.", - default=True) \ No newline at end of file + default=True) diff --git a/blender/arm/logicnode/draw/LN_draw_arc.py b/blender/arm/logicnode/draw/LN_draw_arc.py index 0ca38d8e58..664f70bef7 100644 --- a/blender/arm/logicnode/draw/LN_draw_arc.py +++ b/blender/arm/logicnode/draw/LN_draw_arc.py @@ -15,7 +15,7 @@ class DrawArcNode(ArmLogicTreeNode): @input Radius: The radius of the arc in pixels. @input Start Angle/End Angle: The angles in radians where the arc starts/ends, starting right of the arc's center. - @input Exterior Angle: Whether the angles describe an exterior angle. + @input Exterior Angle: Whether the angles describe an Exterior angle. @output Out: Activated after the arc has been drawn. diff --git a/blender/arm/logicnode/native/LN_call_haxe_static.py b/blender/arm/logicnode/native/LN_call_haxe_static.py index 2a3e382dee..0cbfdb5517 100644 --- a/blender/arm/logicnode/native/LN_call_haxe_static.py +++ b/blender/arm/logicnode/native/LN_call_haxe_static.py @@ -44,4 +44,4 @@ def get_replacement_node(self, node_tree: bpy.types.NodeTree): return NodeReplacement( 'LNCallHaxeStaticNode', self.arm_version, 'LNCallHaxeStaticNode', 2, in_socket_mapping={0:0, 1:1}, out_socket_mapping={0:0, 1:1} - ) \ No newline at end of file + ) diff --git a/blender/arm/make_renderpath.py b/blender/arm/make_renderpath.py index 12b81cb504..f355f38c10 100644 --- a/blender/arm/make_renderpath.py +++ b/blender/arm/make_renderpath.py @@ -74,6 +74,7 @@ def add_world_defs(): if rpdat.rp_shadowmap_atlas_lod: assets.add_khafile_def('arm_shadowmap_atlas_lod') assets.add_khafile_def('rp_shadowmap_atlas_lod_subdivisions={0}'.format(int(rpdat.rp_shadowmap_atlas_lod_subdivisions))) + # SS if rpdat.rp_ssgi == 'RTGI' or rpdat.rp_ssgi == 'RTAO': if rpdat.rp_ssgi == 'RTGI': @@ -83,21 +84,42 @@ def add_world_defs(): if rpdat.rp_autoexposure: wrd.world_defs += '_AutoExposure' + # GI + voxelgi = False + voxelao = False has_voxels = arm.utils.voxel_support() - if rpdat.rp_voxelao and has_voxels and rpdat.arm_material_model == 'Full': - wrd.world_defs += '_VoxelCones' + rpdat.arm_voxelgi_cones - if rpdat.arm_voxelgi_revoxelize: - assets.add_khafile_def('arm_voxelgi_revox') - if rpdat.arm_voxelgi_camera: - wrd.world_defs += '_VoxelGICam' - if rpdat.arm_voxelgi_temporal: - assets.add_khafile_def('arm_voxelgi_temporal') - wrd.world_defs += '_VoxelGITemporal' - wrd.world_defs += '_VoxelAOvar' # Write a shader variant + if has_voxels and rpdat.arm_material_model == 'Full': + if rpdat.rp_voxels == 'Voxel GI': + voxelgi = True + elif rpdat.rp_voxels == 'Voxel AO': + voxelao = True + + if voxelgi or voxelao: + assets.add_shader_external(arm.utils.get_sdk_path() + '/armory/Shaders/voxel_offsetprev/voxel_offsetprev.comp.glsl') + assets.add_shader_external(arm.utils.get_sdk_path() + '/armory/Shaders/voxel_temporal/voxel_temporal.comp.glsl') + assets.add_shader_external(arm.utils.get_sdk_path() + '/armory/Shaders/voxel_sdf_jumpflood/voxel_sdf_jumpflood.comp.glsl') + wrd.world_defs += "_VoxelCones" + rpdat.arm_voxelgi_cones if rpdat.arm_voxelgi_shadows: wrd.world_defs += '_VoxelShadow' - if rpdat.arm_voxelgi_occ == 0.0: - wrd.world_defs += '_VoxelAONoTrace' + assets.add_khafile_def('arm_voxelgi_shadows') + + if voxelgi: + assets.add_shader_external(arm.utils.get_sdk_path() + '/armory/Shaders/voxel_light/voxel_light.comp.glsl') + if rpdat.rp_renderer == "Deferred": + assets.add_shader_external(arm.utils.get_sdk_path() + '/armory/Shaders/voxel_resolve_diffuse/voxel_resolve_diffuse.comp.glsl') + assets.add_shader_external(arm.utils.get_sdk_path() + '/armory/Shaders/voxel_resolve_specular/voxel_resolve_specular.comp.glsl') + wrd.world_defs += '_VoxelGI' + #if rpdat.arm_voxelgi_refraction: + # wrd.world_defs += '_VoxelRefract' + # assets.add_khafile_def('rp_voxelgi_refract') + + elif voxelao: + if rpdat.rp_renderer == "Deferred": + assets.add_shader_external(arm.utils.get_sdk_path() + '/armory/Shaders/voxel_resolve_ao/voxel_resolve_ao.comp.glsl') + wrd.world_defs += '_VoxelAOvar' # Write a shader variant + if rpdat.arm_voxelgi_occ == 0.0: + wrd.world_defs += '_VoxelAONoTrace' + if arm.utils.get_legacy_shaders() or 'ios' in state.target: wrd.world_defs += '_Legacy' @@ -133,6 +155,7 @@ def add_world_defs(): if '_Rad' in wrd.world_defs and '_Brdf' not in wrd.world_defs: wrd.world_defs += '_Brdf' + assets.add_khafile_def("arm_brdf") def build(): rpdat = arm.utils.get_rp() @@ -331,8 +354,8 @@ def build(): wrd.world_defs += '_VR' has_voxels = arm.utils.voxel_support() - if rpdat.rp_voxelao and has_voxels and rpdat.arm_material_model == 'Full': - assets.add_khafile_def('rp_voxels') + if rpdat.rp_voxels != "Off" and has_voxels and rpdat.arm_material_model == 'Full': + assets.add_khafile_def('rp_voxels={0}'.format(rpdat.rp_voxels)) assets.add_khafile_def('rp_voxelgi_resolution={0}'.format(rpdat.rp_voxelgi_resolution)) assets.add_khafile_def('rp_voxelgi_resolution_z={0}'.format(rpdat.rp_voxelgi_resolution_z)) @@ -388,7 +411,7 @@ def build(): wrd.world_defs += '_SSS' assets.add_shader_pass('sss_pass') - if (rpdat.rp_ssr and rpdat.arm_ssr_half_res) or (rpdat.rp_ssgi != 'Off' and rpdat.arm_ssgi_half_res): + if (rpdat.rp_ssr and rpdat.arm_ssr_half_res) or (rpdat.rp_ssgi != 'Off' and rpdat.arm_ssgi_half_res) or rpdat.rp_voxels != "Off": assets.add_shader_pass('downsample_depth') if rpdat.rp_motionblur != 'Off': diff --git a/blender/arm/make_world.py b/blender/arm/make_world.py index c239d45122..1c01361c5f 100644 --- a/blender/arm/make_world.py +++ b/blender/arm/make_world.py @@ -82,6 +82,7 @@ def build(): if write_radiance: # Set world def, everything else is handled by write_probes() wrd.world_defs += '_Rad' + assets.add_khafile_def("arm_radiance") write_probes.check_last_cmft_time() @@ -184,6 +185,7 @@ def build_node_tree(world: bpy.types.World, frag: Shader, vert: Shader, con: Sha solid_mat = rpdat.arm_material_model == 'Solid' if rpdat.arm_irradiance and not solid_mat: world.world_defs += '_Irr' + assets.add_khafile_def("arm_irradiance") col = world.color world.arm_envtex_color = [col[0], col[1], col[2], 1.0] world.arm_envtex_strength = 1.0 diff --git a/blender/arm/material/cycles.py b/blender/arm/material/cycles.py index 875d44328c..55ea327335 100644 --- a/blender/arm/material/cycles.py +++ b/blender/arm/material/cycles.py @@ -259,6 +259,7 @@ def parse_shader(node: bpy.types.Node, socket: bpy.types.NodeSocket) -> Tuple[st mat_state.emission_type = mat_state.EmissionType.SHADED if state.parse_opacity: state.out_opacity = parse_value_input(node.inputs[1]) + state.out_ior = 1.450; else: return parse_group(node, socket) diff --git a/blender/arm/material/cycles_nodes/nodes_texture.py b/blender/arm/material/cycles_nodes/nodes_texture.py index de99c7fe58..c99c520e82 100644 --- a/blender/arm/material/cycles_nodes/nodes_texture.py +++ b/blender/arm/material/cycles_nodes/nodes_texture.py @@ -507,6 +507,7 @@ def parse_tex_environment(node: bpy.types.ShaderNodeTexEnvironment, out_socket: # Append LDR define if disable_hdr: world.world_defs += '_EnvLDR' + assets.add_khafile_def("arm_envldr") wrd = bpy.data.worlds['Arm'] mobile_mat = rpdat.arm_material_model == 'Mobile' or rpdat.arm_material_model == 'Solid' diff --git a/blender/arm/material/make_cluster.py b/blender/arm/material/make_cluster.py index 553e06fd5b..c46194d4af 100644 --- a/blender/arm/material/make_cluster.py +++ b/blender/arm/material/make_cluster.py @@ -73,6 +73,7 @@ def write(vert: shader.Shader, frag: shader.Shader): frag.write(' roughness,') frag.write(' specular,') frag.write(' f0') + if is_shadows: frag.write('\t, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0') # bias if '_Spot' in wrd.world_defs: @@ -82,8 +83,11 @@ def write(vert: shader.Shader, frag: shader.Shader): frag.write('\t, lightsArraySpot[li * 2].xyz') # spotDir frag.write('\t, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w)') # scale frag.write('\t, lightsArraySpot[li * 2 + 1].xyz') # right - if '_VoxelShadow' in wrd.world_defs and '_VoxelAOvar' in wrd.world_defs: - frag.write('\t, voxels, voxpos') + if '_VoxelShadow' in wrd.world_defs: + frag.write(', voxels') + frag.write(', voxelsSDF') + frag.write(', clipmaps') + if '_MicroShadowing' in wrd.world_defs and not is_mobile: frag.write('\t, occlusion') frag.write(');') diff --git a/blender/arm/material/make_depth.py b/blender/arm/material/make_depth.py index 97679fbe33..21e3e49b94 100644 --- a/blender/arm/material/make_depth.py +++ b/blender/arm/material/make_depth.py @@ -53,7 +53,7 @@ def make(context_id, rpasses, shadowmap=False): if parse_opacity: frag.write('float opacity;') frag.write('float ior;') - + if(con_depth).is_elem('morph'): make_morph_target.morph_pos(vert) diff --git a/blender/arm/material/make_mesh.py b/blender/arm/material/make_mesh.py index 2933a02cca..fbd1cfe851 100644 --- a/blender/arm/material/make_mesh.py +++ b/blender/arm/material/make_mesh.py @@ -116,6 +116,7 @@ def make_base(con_mesh, parse_opacity): vattr_written = False rpdat = arm.utils.get_rp() is_displacement = mat_utils.disp_linked(mat_state.output_node) + wrd = bpy.data.worlds['Arm'] if is_displacement: if rpdat.arm_rp_displacement == 'Vertex': frag.ins = vert.outs @@ -195,7 +196,7 @@ def make_deferred(con_mesh, rpasses): rpdat = arm.utils.get_rp() arm_discard = mat_state.material.arm_discard - parse_opacity = arm_discard or 'translucent' + parse_opacity = arm_discard or 'translucent' or 'refraction' in rpasses make_base(con_mesh, parse_opacity=parse_opacity) @@ -637,39 +638,58 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False): if '_Irr' in wrd.world_defs: frag.add_include('std/shirr.glsl') frag.add_uniform('vec4 shirr[7]', link='_envmapIrradiance') - frag.write('vec3 indirect = shIrradiance(n, shirr);') + frag.write('vec3 envl = shIrradiance(n, shirr);') if '_EnvTex' in wrd.world_defs: - frag.write('indirect /= PI;') - frag.write('indirect *= albedo;') - if '_Rad' in wrd.world_defs: - frag.add_uniform('sampler2D senvmapRadiance', link='_envmapRadiance') - frag.add_uniform('int envmapNumMipmaps', link='_envmapNumMipmaps') - frag.write('vec3 reflectionWorld = reflect(-vVec, n);') - frag.write('float lod = getMipFromRoughness(roughness, envmapNumMipmaps);') - frag.write('vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;') - if '_EnvLDR' in wrd.world_defs: - frag.write('prefilteredColor = pow(prefilteredColor, vec3(2.2));') - frag.write('indirect += prefilteredColor * (f0 * envBRDF.x + envBRDF.y) * 1.5;') - elif '_EnvCol' in wrd.world_defs: - frag.add_uniform('vec3 backgroundCol', link='_backgroundCol') - frag.write('indirect += backgroundCol * f0;') + frag.write('envl /= PI;') else: - frag.write('vec3 indirect = albedo;') - frag.write('indirect *= occlusion;') + frag.write('vec3 envl = vec3(0.0);') + + if '_Rad' in wrd.world_defs: + frag.add_uniform('sampler2D senvmapRadiance', link='_envmapRadiance') + frag.add_uniform('int envmapNumMipmaps', link='_envmapNumMipmaps') + frag.write('vec3 reflectionWorld = reflect(-eyeDir, n);') + frag.write('float lod = getMipFromRoughness(roughness, envmapNumMipmaps);') + frag.write('vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;') + + if '_EnvLDR' in wrd.world_defs: + frag.write('envl = pow(envl, vec3(2.2));') + if '_Rad' in wrd.world_defs: + frag.write('prefilteredColor = pow(prefilteredColor, vec3(2.2));') + + frag.write('envl *= albedo;') + + if '_Brdf' in wrd.world_defs: + frag.write('envl.rgb *= 1.0 - (f0 * envBRDF.x + envBRDF.y);') + if '_Rad' in wrd.world_defs: + frag.write('envl += prefilteredColor * (f0 * envBRDF.x + envBRDF.y);') + elif '_EnvCol' in wrd.world_defs: + frag.add_uniform('vec3 backgroundCol', link='_backgroundCol') + frag.write('envl += backgroundCol * (f0 * envBRDF.x + envBRDF.y);') frag.add_uniform('float envmapStrength', link='_envmapStrength') - frag.write('indirect *= envmapStrength;') + frag.write('envl *= envmapStrength * occlusion;') - if '_VoxelAOvar' in wrd.world_defs: + #if '_VoxelGI' in wrd.world_defs: + # frag.write('vec3 indirect = vec3(0.0);') + #else: + frag.write('vec3 indirect = envl;') + + if '_VoxelGI' in wrd.world_defs or '_VoxelAOvar' in wrd.world_defs: frag.add_include('std/conetrace.glsl') frag.add_uniform('sampler3D voxels') - if '_VoxelGICam' in wrd.world_defs: - frag.add_uniform('vec3 eyeSnap', link='_cameraPositionSnap') - frag.write('vec3 voxpos = (wposition - eyeSnap) / voxelgiHalfExtents;') - else: - frag.write('vec3 voxpos = wposition / voxelgiHalfExtents;') - frag.write('indirect *= vec3(1.0 - traceAO(voxpos, n, voxels));') + if '_VoxelGI' in wrd.world_defs or '_VoxelShadow' in wrd.world_defs: + frag.add_uniform('sampler3D voxelsSDF') + frag.add_uniform('float clipmaps[voxelgiClipmapCount * 10]', link='_clipmaps') + if '_VoxelAOvar' in wrd.world_defs: + frag.write('indirect *= (1.0 - traceAO(wposition, n, voxels, clipmaps).r);') + + if '_VoxelGI' in wrd.world_defs: + frag.write('indirect += traceDiffuse(wposition, n, voxels, clipmaps).rgb * albedo * voxelgiDiff;') + frag.write('if (roughness < 1.0 && specular > 0.0) {') + frag.write(' vec2 pixel = gl_FragCoord.xy;') + frag.write(' indirect += traceSpecular(wposition, n, voxels, voxelsSDF, vVec, roughness, clipmaps, pixel).rgb * specular * voxelgiRefl;') + frag.write('}') frag.write('vec3 direct = vec3(0.0);') if '_Sun' in wrd.world_defs: @@ -708,8 +728,8 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False): frag.write('const vec2 smSize = shadowmapSize;') frag.write(f'svisibility = PCF({shadowmap_sun}, lPos.xy, lPos.z - shadowsBias, smSize);') frag.write('}') # receiveShadow - if '_VoxelShadow' in wrd.world_defs and '_VoxelAOvar' in wrd.world_defs: - frag.write('svisibility *= 1.0 - traceShadow(voxels, voxpos, sunDir);') + if '_VoxelShadow' in wrd.world_defs: + frag.write('svisibility *= 1.0 - traceShadow(wposition, n, voxels, voxelsSDF, sunDir, clipmaps);') frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;') # sun @@ -733,13 +753,15 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False): frag.write('direct += sampleLight(') frag.write(' wposition, n, vVec, dotNV, pointPos, pointCol, albedo, roughness, specular, f0') if is_shadows: - frag.write(' , 0, pointBias, receiveShadow') + frag.write(', 0, pointBias, receiveShadow') if '_Spot' in wrd.world_defs: - frag.write(' , true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight') - if '_VoxelShadow' in wrd.world_defs and '_VoxelAOvar' in wrd.world_defs: - frag.write(' , voxels, voxpos') + frag.write(', true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight') + if '_VoxelShadow' in wrd.world_defs: + frag.write(', voxels') + frag.write(', voxelsSDF') + frag.write(', clipmaps') if '_MicroShadowing' in wrd.world_defs: - frag.write(' , occlusion') + frag.write(', occlusion') frag.write(');') if '_Clusters' in wrd.world_defs: @@ -749,8 +771,12 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False): if mat_state.emission_type == mat_state.EmissionType.SHADELESS: frag.write('direct = vec3(0.0);') frag.write('indirect += emissionCol;') - - + """ + if '_VoxelRefract' in wrd.world_defs and parse_opacity: + frag.write('vec3 refraction = traceRefraction(wposition, n, voxels, eyeDir, ior, roughness, eye) * voxelgiRefr;') + frag.write('indirect = mix(refraction, indirect, opacity);') + frag.write('direct = mix(refraction, direct, opacity);') + """ def _write_material_attribs_default(frag: shader.Shader, parse_opacity: bool): frag.write('vec3 basecol;') frag.write('float roughness;') diff --git a/blender/arm/material/make_tess.py b/blender/arm/material/make_tess.py index ade6e3a5b5..278813bc77 100644 --- a/blender/arm/material/make_tess.py +++ b/blender/arm/material/make_tess.py @@ -18,7 +18,7 @@ def interpolate(tese, var, size, normalize=False, declare_out=False): s = '{0} {1}_0 = gl_TessCoord.x * tc_{1}[0];\n'.format(vec, var) s += '{0} {1}_1 = gl_TessCoord.y * tc_{1}[1];\n'.format(vec, var) s += '{0} {1}_2 = gl_TessCoord.z * tc_{1}[2];\n'.format(vec, var) - + prep = '' if not declare_out: prep = vec + ' ' diff --git a/blender/arm/material/make_voxel.py b/blender/arm/material/make_voxel.py index 9a5aa91ebb..1546c65673 100644 --- a/blender/arm/material/make_voxel.py +++ b/blender/arm/material/make_voxel.py @@ -1,5 +1,34 @@ +""" +Copyright (c) 2024 Turánszki János + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" import bpy +import arm.utils +import arm.assets as assets +import arm.material.cycles as cycles +import arm.material.mat_state as mat_state +import arm.material.mat_utils as mat_utils +import arm.material.make_particle as make_particle +import arm.make_state as state + import arm.utils import arm.assets as assets import arm.material.mat_state as mat_state @@ -11,10 +40,12 @@ else: arm.enable_reload(__name__) - def make(context_id): rpdat = arm.utils.get_rp() - con = make_ao(context_id) + if rpdat.rp_voxels == 'Voxel GI': + con = make_gi(context_id) + else: + con = make_ao(context_id) assets.vs_equal(con, assets.shader_cons['voxel_vert']) assets.fs_equal(con, assets.shader_cons['voxel_frag']) @@ -22,6 +53,211 @@ def make(context_id): return con +def make_gi(context_id): + con_voxel = mat_state.data.add_context({ 'name': context_id, 'depth_write': False, 'compare_mode': 'always', 'cull_mode': 'none', 'color_write_red': False, 'color_write_green': False, 'color_write_blue': False, 'color_write_alpha': False, 'conservative_raster': True }) + wrd = bpy.data.worlds['Arm'] + + vert = con_voxel.make_vert() + frag = con_voxel.make_frag() + geom = con_voxel.make_geom() + tesc = None + tese = None + geom.ins = vert.outs + frag.ins = geom.outs + + vert.add_include('compiled.inc') + geom.add_include('compiled.inc') + frag.add_include('compiled.inc') + frag.add_include('std/math.glsl') + frag.add_include('std/imageatomic.glsl') + frag.add_include('std/gbuffer.glsl') + frag.add_include('std/brdf.glsl') + + rpdat = arm.utils.get_rp() + frag.add_uniform('layout(r32ui) uimage3D voxels') + + frag.write('vec3 wposition;') + frag.write('vec3 basecol;') + frag.write('float roughness;') # + frag.write('float metallic;') # + frag.write('float occlusion;') # + frag.write('float specular;') # + frag.write('vec3 emissionCol = vec3(0.0);') + parse_opacity = rpdat.arm_voxelgi_refraction + if parse_opacity: + frag.write('float opacity;') + frag.write('float ior;') + else: + frag.write('float opacity = 1.0;') + + frag.write('float dotNV = 0.0;') + cycles.parse(mat_state.nodes, con_voxel, vert, frag, geom, tesc, tese, parse_opacity=False, parse_displacement=False, basecol_only=True) + + # Voxelized particles + particle = mat_state.material.arm_particle_flag + if particle and rpdat.arm_particles == 'On': + # make_particle.write(vert, particle_info=cycles.particle_info) + frag.write_pre = True + frag.write('const float p_index = 0;') + frag.write('const float p_age = 0;') + frag.write('const float p_lifetime = 0;') + frag.write('const vec3 p_location = vec3(0);') + frag.write('const float p_size = 0;') + frag.write('const vec3 p_velocity = vec3(0);') + frag.write('const vec3 p_angular_velocity = vec3(0);') + frag.write_pre = False + + export_mpos = frag.contains('mposition') and not frag.contains('vec3 mposition') + if export_mpos: + vert.add_out('vec3 mpositionGeom') + vert.write_pre = True + vert.write('mpositionGeom = pos.xyz;') + vert.write_pre = False + + export_bpos = frag.contains('bposition') and not frag.contains('vec3 bposition') + if export_bpos: + vert.add_out('vec3 bpositionGeom') + vert.add_uniform('vec3 dim', link='_dim') + vert.add_uniform('vec3 hdim', link='_halfDim') + vert.write_pre = True + vert.write('bpositionGeom = (pos.xyz + hdim) / dim;') + vert.write_pre = False + + vert.add_uniform('mat4 W', '_worldMatrix') + vert.add_uniform('mat3 N', '_normalMatrix') + vert.add_out('vec3 voxpositionGeom') + vert.add_out('vec3 voxnormalGeom') + + if con_voxel.is_elem('col'): + vert.add_out('vec3 vcolorGeom') + vert.write('vcolorGeom = col.rgb;') + + if con_voxel.is_elem('tex'): + vert.add_out('vec2 texCoordGeom') + vert.write('texCoordGeom = tex;') + + vert.write('voxpositionGeom = vec3(W * vec4(pos.xyz, 1.0));') + vert.write('voxnormalGeom = normalize(N * vec3(nor.xy, pos.w));') + + geom.add_out('vec4 voxposition[3]') + geom.add_out('vec3 P') + geom.add_out('vec3 voxnormal') + geom.add_out('vec4 lightPosition') + geom.add_out('vec4 spotPosition') + geom.add_out('vec4 wvpposition') + + if con_voxel.is_elem('col'): + geom.add_out('vec3 vcolor') + if con_voxel.is_elem('tex'): + geom.add_out('vec2 texCoord') + if export_mpos: + geom.add_out('vec3 mposition') + if export_bpos: + geom.add_out('vec3 bposition') + + geom.add_uniform('float clipmaps[voxelgiClipmapCount * 10]', '_clipmaps') + geom.add_uniform('int clipmapLevel', '_clipmapLevel') + + geom.write('vec3 facenormal = abs(voxnormalGeom[0] + voxnormalGeom[1] + voxnormalGeom[2]);') + geom.write('uint maxi = facenormal[1] > facenormal[0] ? 1 : 0;') + geom.write('maxi = facenormal[2] > facenormal[maxi] ? 2 : maxi;') + + geom.write('for (uint i = 0; i < 3; ++i) {') + geom.write(' voxposition[i].xyz = (voxpositionGeom[i] - vec3(clipmaps[int(clipmapLevel * 10 + 4)], clipmaps[int(clipmapLevel * 10 + 5)], clipmaps[int(clipmapLevel * 10 + 6)])) / (float(clipmaps[int(clipmapLevel * 10)]));') + geom.write(' if (maxi == 0)') + geom.write(' {') + geom.write(' voxposition[i].xyz = voxposition[i].zyx;') + geom.write(' }') + geom.write(' else if (maxi == 1)') + geom.write(' {') + geom.write(' voxposition[i].xyz = voxposition[i].xzy;') + geom.write(' }') + geom.write('}') + + geom.write('for (uint i = 0; i < 3; ++i) {') + geom.write(' voxposition[i].xy /= voxelgiResolution.xy;') + geom.write(' voxposition[i].zw = vec2(1.0);') + geom.write(' P = voxpositionGeom[i];') + geom.write(' voxnormal = voxnormalGeom[i];') + if con_voxel.is_elem('col'): + geom.write('vcolor = vcolorGeom[i];') + if con_voxel.is_elem('tex'): + geom.write('texCoord = texCoordGeom[i];') + if export_mpos: + geom.write('mposition = mpositionGeom[i];') + if export_bpos: + geom.write('bposition = bpositionGeom[i];') + geom.write(' gl_Position = voxposition[i];') + geom.write(' EmitVertex();') + geom.write('}') + geom.write('EndPrimitive();') + + frag.add_uniform('float clipmaps[voxelgiClipmapCount * 10]', '_clipmaps') + frag.add_uniform('int clipmapLevel', '_clipmapLevel') + + frag.write('vec3 uvw = (P - vec3(clipmaps[int(clipmapLevel * 10 + 4)], clipmaps[int(clipmapLevel * 10 + 5)], clipmaps[int(clipmapLevel * 10 + 6)])) / (float(clipmaps[int(clipmapLevel * 10)]) * voxelgiResolution);') + frag.write('uvw = (uvw * 0.5 + 0.5);') + frag.write('if(any(notEqual(uvw, clamp(uvw, 0.0, 1.0)))) return;') + frag.write('vec3 writecoords = floor(uvw * voxelgiResolution);') + frag.write_attrib('vec3 n = normalize(voxnormal);') + frag.write('vec3 aniso_direction = n;') + frag.write('uvec3 face_offsets = uvec3(') + frag.write(' aniso_direction.x > 0 ? 0 : 1,') + frag.write(' aniso_direction.y > 0 ? 2 : 3,') + frag.write(' aniso_direction.z > 0 ? 4 : 5') + frag.write(' ) * voxelgiResolution;') + frag.write('vec3 direction_weights = abs(n);') + + frag.write('if (direction_weights.x > 0) {') + frag.write(' vec4 basecol_direction = vec4(min(basecol * direction_weights.x, vec3(1.0)), opacity);') + frag.write(' vec3 emission_direction = min(emissionCol * direction_weights.x, vec3(1.0));') + frag.write(' vec2 normal_direction = encode_oct(n * direction_weights.x) * 0.5 + 0.5;') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.x, 0, 0)), uint(basecol_direction.r * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.x, 0, voxelgiResolution.x)), uint(basecol_direction.g * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.x, 0, voxelgiResolution.x * 2)), uint(basecol_direction.b * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.x, 0, voxelgiResolution.x * 3)), uint(basecol_direction.a * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.x, 0, voxelgiResolution.x * 4)), uint(emission_direction.r * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.x, 0, voxelgiResolution.x * 5)), uint(emission_direction.g * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.x, 0, voxelgiResolution.x * 6)), uint(emission_direction.b * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.x, 0, voxelgiResolution.x * 7)), uint(normal_direction.r * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.x, 0, voxelgiResolution.x * 8)), uint(normal_direction.g * 255));') + frag.write('}') + + + frag.write('if (direction_weights.y > 0) {') + frag.write(' vec4 basecol_direction = vec4(min(basecol * direction_weights.y, vec3(1.0)), opacity);') + frag.write(' vec3 emission_direction = min(emissionCol * direction_weights.y, vec3(1.0));') + frag.write(' vec2 normal_direction = encode_oct(n * direction_weights.y) * 0.5 + 0.5;') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.y, 0, 0)), uint(basecol_direction.r * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.y, 0, voxelgiResolution.x)), uint(basecol_direction.g * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.y, 0, voxelgiResolution.x * 2)), uint(basecol_direction.b * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.y, 0, voxelgiResolution.x * 3)), uint(basecol_direction.a * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.y, 0, voxelgiResolution.x * 4)), uint(emission_direction.r * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.y, 0, voxelgiResolution.x * 5)), uint(emission_direction.g * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.y, 0, voxelgiResolution.x * 6)), uint(emission_direction.b * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.y, 0, voxelgiResolution.x * 7)), uint(normal_direction.r * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.y, 0, voxelgiResolution.x * 8)), uint(normal_direction.g * 255));') + + frag.write('}') + + frag.write('if (direction_weights.z > 0) {') + frag.write(' vec4 basecol_direction = vec4(min(basecol * direction_weights.z, vec3(1.0)), opacity);') + frag.write(' vec3 emission_direction = min(emissionCol * direction_weights.z, vec3(1.0));') + frag.write(' vec2 normal_direction = encode_oct(n * direction_weights.z) * 0.5 + 0.5;') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.z, 0, 0)), uint(basecol_direction.r * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.z, 0, voxelgiResolution.x)), uint(basecol_direction.g * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.z, 0, voxelgiResolution.x * 2)), uint(basecol_direction.b * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.z, 0, voxelgiResolution.x * 3)), uint(basecol_direction.a * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.z, 0, voxelgiResolution.x * 4)), uint(emission_direction.r * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.z, 0, voxelgiResolution.x * 5)), uint(emission_direction.g * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.z, 0, voxelgiResolution.x * 6)), uint(emission_direction.b * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.z, 0, voxelgiResolution.x * 7)), uint(normal_direction.r * 255));') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.z, 0, voxelgiResolution.x * 8)), uint(normal_direction.g * 255));') + frag.write('}') + + return con_voxel + + def make_ao(context_id): con_voxel = mat_state.data.add_context({ 'name': context_id, 'depth_write': False, 'compare_mode': 'always', 'cull_mode': 'none', 'color_writes_red': [False], 'color_writes_green': [False], 'color_writes_blue': [False], 'color_writes_alpha': [False], 'conservative_raster': False }) wrd = bpy.data.worlds['Arm'] @@ -33,37 +269,60 @@ def make_ao(context_id): tesc = None tese = None + geom.ins = vert.outs + frag.ins = geom.outs + + frag.add_include('compiled.inc') + geom.add_include('compiled.inc') + frag.add_include('std/math.glsl') + frag.add_include('std/imageatomic.glsl') + frag.write_header('#extension GL_ARB_shader_image_load_store : enable') + + vert.add_include('compiled.inc') + vert.add_uniform('mat4 W', '_worldMatrix') + vert.add_uniform('mat3 N', '_normalMatrix') + + geom.add_uniform('float clipmaps[voxelgiClipmapCount * 10]', '_clipmaps') + geom.add_uniform('int clipmapLevel', '_clipmapLevel') + + frag.add_uniform('float clipmaps[voxelgiClipmapCount * 10]', '_clipmaps') + frag.add_uniform('int clipmapLevel', '_clipmapLevel') + + """ if arm.utils.get_gapi() == 'direct3d11': for e in con_voxel.data['vertex_elements']: if e['name'] == 'nor': con_voxel.data['vertex_elements'].remove(e) break - # No geom shader compiler for hlsl yet - vert.noprocessing = True - frag.noprocessing = True - geom.noprocessing = True - - vert.add_uniform('mat4 W', '_worldMatrix') vert.write('uniform float4x4 W;') - if rpdat.arm_voxelgi_revoxelize and rpdat.arm_voxelgi_camera: - vert.add_uniform('vec3 eyeSnap', '_cameraPositionSnap') - vert.write('uniform float3 eyeSnap;') - vert.write('struct SPIRV_Cross_Input { float4 pos : TEXCOORD0; };') - vert.write('struct SPIRV_Cross_Output { float4 svpos : SV_POSITION; };') + vert.write('uniform float3x3 N;') + vert.write('struct SPIRV_Cross_Input {') + vert.write(' float4 pos : TEXCOORD0;') + vert.write(' float3 nor : NORMAL;') + vert.write('};') + vert.write('struct SPIRV_Cross_Output {') + vert.write(' float4 svpos : SV_POSITION;') + vert.write(' float3 svnor : NORMAL;') + vert.write('};') vert.write('SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) {') vert.write(' SPIRV_Cross_Output stage_output;') - voxHalfExt = str(round(rpdat.arm_voxelgi_dimensions / 2.0)) - if rpdat.arm_voxelgi_revoxelize and rpdat.arm_voxelgi_camera: - vert.write(' stage_output.svpos.xyz = (mul(float4(stage_input.pos.xyz, 1.0), W).xyz - eyeSnap) / float3(' + voxHalfExt + ', ' + voxHalfExt + ', ' + voxHalfExt + ');') - else: - vert.write(' stage_output.svpos.xyz = mul(float4(stage_input.pos.xyz, 1.0), W).xyz / float3(' + voxHalfExt + ', ' + voxHalfExt + ', ' + voxHalfExt + ');') + vert.write(' stage_output.svpos.xyz = mul(float4(stage_input.pos.xyz, 1.0), W).xyz;') vert.write(' stage_output.svpos.w = 1.0;') + vert.write(' stage_output.svnor.xyz = normalize(mul(float3(nor.xy, pos.w), N).xyz);') vert.write(' return stage_output;') vert.write('}') - geom.write('struct SPIRV_Cross_Input { float4 svpos : SV_POSITION; };') - geom.write('struct SPIRV_Cross_Output { float3 wpos : TEXCOORD0; float4 svpos : SV_POSITION; };') + geom.write('uniform float clipmaps[voxelgiClipmapCount * 10];') + geom.write('uniform int clipmapLevel;') + geom.write('struct SPIRV_Cross_Input {') + geom.write(' float4 svpos : SV_POSITION;') + geom.write(' float3 svnor : NORMAL;') + geom.write('};') + geom.write('struct SPIRV_Cross_Output {') + geom.write(' float3 wpos : TEXCOORD0;') + geom.write(' float3 wnor : NORMAL;') + geom.write('};') geom.write('[maxvertexcount(3)]') geom.write('void main(triangle SPIRV_Cross_Input stage_input[3], inout TriangleStream output) {') geom.write(' float3 p1 = stage_input[1].svpos.xyz - stage_input[0].svpos.xyz;') @@ -71,7 +330,8 @@ def make_ao(context_id): geom.write(' float3 p = abs(cross(p1, p2));') geom.write(' for (int i = 0; i < 3; ++i) {') geom.write(' SPIRV_Cross_Output stage_output;') - geom.write(' stage_output.wpos = stage_input[i].svpos.xyz;') + geom.write(' stage_output.wpos = (stage_input[i].svpos.xyz + float3(clipmaps[int(clipmapLevel * 10 + 4)], clipmaps[int(clipmapLevel * 10 + 5)], clipmaps[int(clipmapLevel * 10 + 6)])) / (float(clipmaps[clipmapLevel * 10]) * voxelgiResolution);') + geom.write(' stage_output.wnor = stage_input[i].svnor.xyz;') geom.write(' if (p.z > p.x && p.z > p.y) {') geom.write(' stage_output.svpos = float4(stage_input[i].svpos.x, stage_input[i].svpos.y, 0.0, 1.0);') geom.write(' }') @@ -87,56 +347,104 @@ def make_ao(context_id): frag.add_uniform('layout(r8) writeonly image3D voxels') frag.write('RWTexture3D voxels;') - frag.write('struct SPIRV_Cross_Input { float3 wpos : TEXCOORD0; };') + frag.write('uniform float clipmaps[voxelgiClipmapCount * 10];') + frag.write('uniform int clipmapLevel;') + + frag.write('struct SPIRV_Cross_Input {') + frag.write(' float3 wpos : TEXCOORD0;') + frag.write(' float3 wnor : NORMAL;') + frag.write('};') frag.write('struct SPIRV_Cross_Output { float4 FragColor : SV_TARGET0; };') frag.write('void main(SPIRV_Cross_Input stage_input) {') - frag.write(' if (abs(stage_input.wpos.z) > ' + rpdat.rp_voxelgi_resolution_z + ' || abs(stage_input.wpos.x) > 1 || abs(stage_input.wpos.y) > 1) return;') - voxRes = str(rpdat.rp_voxelgi_resolution) - voxResZ = str(int(int(rpdat.rp_voxelgi_resolution) * float(rpdat.rp_voxelgi_resolution_z))) - frag.write(' voxels[int3(' + voxRes + ', ' + voxRes + ', ' + voxResZ + ') * (stage_input.wpos * 0.5 + 0.5)] = 1.0;') - frag.write('') + frag.write(' float3 uvw = (stage_input.wpos.xyz - float3(clipmaps[int(clipmapLevel * 10 + 4)], clipmaps[int(clipmapLevel * 10 + 5)], clipmaps[int(clipmapLevel * 10 + 6)])) / (float(clipmaps[int(clipmapLevel * 10)]) * voxelgiResolution);') + frag.write(' uvw = uvw * 0.5 + 0.5;') + frag.write(' if(any(!saturate(uvw))) return;') + frag.write(' uvw = floor(uvw * voxelgiResolution);') + frag.write(' uint3 face_offsets = uint3(') + frag.write(' stage_input.wnor.x > 0 ? 0 : 1,') + frag.write(' stage_input.wnor.y > 0 ? 2 : 3,') + frag.write(' stage_input.wnor.z > 0 ? 4 : 5') + frag.write(' ) * voxelgiResolution;') + frag.write(' float3 direction_weights = abs(stage_input.wnor);') + + frag.write(' if (direction_weights.x > 0.0) {') + frag.write(' float opac_direction = direction_weights.x;') + frag.write(' voxels[uvw + int3(face_offsets.x, 0, 0))] = float4(opac_direction);') + frag.write(' }') + + frag.write(' if (direction_weights.y > 0.0) {') + frag.write(' float opac_direction = direction_weights.y;') + frag.write(' voxels[uvw + int3(face_offsets.y, 0, 0))] = float4(opac_direction);') + frag.write(' }') + + frag.write(' if (direction_weights.z > 0.0) {') + frag.write(' float opac_direction = direction_weights.z;') + frag.write(' voxels[uvw + int3(face_offsets.z, 0, 0))] = float4(opac_direction);') + frag.write(' }') frag.write('}') else: - geom.ins = vert.outs - frag.ins = geom.outs + """ + frag.add_uniform('layout(r32ui) uimage3D voxels') - frag.add_include('compiled.inc') - frag.add_include('std/math.glsl') - frag.add_include('std/imageatomic.glsl') - frag.write_header('#extension GL_ARB_shader_image_load_store : enable') + vert.add_out('vec3 voxpositionGeom') + vert.add_out('vec3 voxnormalGeom') - frag.add_uniform('layout(r8) writeonly image3D voxels') + vert.write('voxpositionGeom = vec3(W * vec4(pos.xyz, 1.0));') + vert.write('voxnormalGeom = normalize(N * vec3(nor.xy, pos.w));') - vert.add_include('compiled.inc') - vert.add_uniform('mat4 W', '_worldMatrix') - vert.add_out('vec3 voxpositionGeom') - - if rpdat.arm_voxelgi_revoxelize and rpdat.arm_voxelgi_camera: - vert.add_uniform('vec3 eyeSnap', '_cameraPositionSnap') - vert.write('voxpositionGeom = (vec3(W * vec4(pos.xyz, 1.0)) - eyeSnap) / voxelgiHalfExtents;') - else: - vert.write('voxpositionGeom = vec3(W * vec4(pos.xyz, 1.0)) / voxelgiHalfExtents;') - - geom.add_out('vec3 voxposition') - geom.write('vec3 p1 = voxpositionGeom[1] - voxpositionGeom[0];') - geom.write('vec3 p2 = voxpositionGeom[2] - voxpositionGeom[0];') - geom.write('vec3 p = abs(cross(p1, p2));') - geom.write('for (uint i = 0; i < 3; ++i) {') - geom.write(' voxposition = voxpositionGeom[i];') - geom.write(' if (p.z > p.x && p.z > p.y) {') - geom.write(' gl_Position = vec4(voxposition.x, voxposition.y, 0.0, 1.0);') - geom.write(' }') - geom.write(' else if (p.x > p.y && p.x > p.z) {') - geom.write(' gl_Position = vec4(voxposition.y, voxposition.z, 0.0, 1.0);') - geom.write(' }') - geom.write(' else {') - geom.write(' gl_Position = vec4(voxposition.x, voxposition.z, 0.0, 1.0);') - geom.write(' }') - geom.write(' EmitVertex();') - geom.write('}') - geom.write('EndPrimitive();') + geom.add_out('vec4 voxposition[3]') + geom.add_out('vec3 P') + geom.add_out('vec3 voxnormal') + + geom.write('vec3 facenormal = abs(voxnormalGeom[0] + voxnormalGeom[1] + voxnormalGeom[2]);') + geom.write('uint maxi = facenormal[1] > facenormal[0] ? 1 : 0;') + geom.write('maxi = facenormal[2] > facenormal[maxi] ? 2 : maxi;') + + geom.write('for (uint i = 0; i < 3; ++i) {') + geom.write(' voxposition[i].xyz = (voxpositionGeom[i] - vec3(clipmaps[int(clipmapLevel * 10 + 4)], clipmaps[int(clipmapLevel * 10 + 5)], clipmaps[int(clipmapLevel * 10 + 6)])) / (float(clipmaps[int(clipmapLevel * 10)]));') + geom.write(' if (maxi == 0)') + geom.write(' {') + geom.write(' voxposition[i].xyz = voxposition[i].zyx;') + geom.write(' }') + geom.write(' else if (maxi == 1)') + geom.write(' {') + geom.write(' voxposition[i].xyz = voxposition[i].xzy;') + geom.write(' }') + geom.write('}') + + geom.write('for (uint i = 0; i < 3; ++i) {') + geom.write(' voxposition[i].xy /= voxelgiResolution.x;') + geom.write(' voxposition[i].zw = vec2(1.0);') + geom.write(' P = voxpositionGeom[i];') + geom.write(' voxnormal = voxnormalGeom[i];') + geom.write(' gl_Position = voxposition[i];') + geom.write(' EmitVertex();') + geom.write('}') + geom.write('EndPrimitive();') + + frag.write('vec3 uvw = (P - vec3(clipmaps[int(clipmapLevel * 10 + 4)], clipmaps[int(clipmapLevel * 10 + 5)], clipmaps[int(clipmapLevel * 10 + 6)])) / (float(clipmaps[int(clipmapLevel * 10)]) * voxelgiResolution);') + frag.write('uvw = uvw * 0.5 + 0.5;') + frag.write('if(any(notEqual(uvw, clamp(uvw, 0.0, 1.0)))) return;') + frag.write('vec3 writecoords = floor(uvw * voxelgiResolution);') + frag.write('vec3 N = normalize(voxnormal);') + frag.write('vec3 aniso_direction = N;') + frag.write('uvec3 face_offsets = uvec3(') + frag.write(' aniso_direction.x > 0 ? 0 : 1,') + frag.write(' aniso_direction.y > 0 ? 2 : 3,') + frag.write(' aniso_direction.z > 0 ? 4 : 5') + frag.write(' ) * voxelgiResolution;') + frag.write('vec3 direction_weights = abs(N);') + + frag.write('if (direction_weights.x > 0) {') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.x, 0, 0)), uint(direction_weights.x * 255));') + frag.write('}') + + frag.write('if (direction_weights.y > 0) {') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.y, 0, 0)), uint(direction_weights.y * 255));') + frag.write('}') - frag.write('if (abs(voxposition.z) > ' + rpdat.rp_voxelgi_resolution_z + ' || abs(voxposition.x) > 1 || abs(voxposition.y) > 1) return;') - frag.write('imageStore(voxels, ivec3(voxelgiResolution * (voxposition * 0.5 + 0.5)), vec4(1.0));') + frag.write('if (direction_weights.z > 0) {') + frag.write(' imageAtomicMax(voxels, ivec3(writecoords + ivec3(face_offsets.z, 0, 0)), uint(direction_weights.z * 255));') + frag.write('}') return con_voxel diff --git a/blender/arm/material/mat_utils.py b/blender/arm/material/mat_utils.py index 2809f6e143..8715032666 100644 --- a/blender/arm/material/mat_utils.py +++ b/blender/arm/material/mat_utils.py @@ -50,7 +50,7 @@ def get_rpasses(material): ar.append('translucent') elif is_transluc(material) and not material.arm_discard and not material.arm_blending and rpdat.rp_ss_refraction: ar.append('refraction') - if rpdat.rp_voxelao and has_voxels: + if rpdat.rp_voxels != "Off" and has_voxels: ar.append('voxel') if rpdat.rp_renderer == 'Forward' and rpdat.rp_depthprepass and not material.arm_blending and not material.arm_particle_flag: ar.append('depth') diff --git a/blender/arm/props.py b/blender/arm/props.py index d5086f2ddf..dd6f99d505 100644 --- a/blender/arm/props.py +++ b/blender/arm/props.py @@ -567,7 +567,7 @@ def update_armory_world(): for rp in wrd.arm_rplist: # TODO: deprecated if rp.rp_gi != 'Off': rp.rp_gi = 'Off' - rp.rp_voxelao = True + rp.rp_voxels = rp.rp_gi # For some breaking changes we need to use a special update # routine first before regularly replacing nodes diff --git a/blender/arm/props_renderpath.py b/blender/arm/props_renderpath.py index 17a7703cef..7e023db1b9 100644 --- a/blender/arm/props_renderpath.py +++ b/blender/arm/props_renderpath.py @@ -64,7 +64,9 @@ def update_preset(self, context): rpdat.rp_hdr = True rpdat.rp_background = 'World' rpdat.rp_stereo = False - rpdat.rp_voxelao = False + rpdat.rp_voxelgi_resolution = '32' + rpdat.arm_voxelgi_size = 0.25 + rpdat.rp_voxels = 'Voxel AO' rpdat.rp_render_to_texture = True rpdat.rp_supersampling = '1' rpdat.rp_antialiasing = 'SMAA' @@ -102,7 +104,7 @@ def update_preset(self, context): rpdat.rp_hdr = False rpdat.rp_background = 'Clear' rpdat.rp_stereo = False - rpdat.rp_voxelao = False + rpdat.rp_voxels = 'Off' rpdat.rp_render_to_texture = False rpdat.rp_supersampling = '1' rpdat.rp_antialiasing = 'Off' @@ -138,10 +140,12 @@ def update_preset(self, context): rpdat.rp_hdr = True rpdat.rp_background = 'World' rpdat.rp_stereo = False - rpdat.rp_voxelao = True - rpdat.rp_voxelgi_resolution = '128' + rpdat.rp_voxels = 'Voxel GI' + rpdat.rp_voxelgi_resolution = '64' + rpdat.arm_voxelgi_size = 0.125 rpdat.arm_voxelgi_revoxelize = False rpdat.arm_voxelgi_camera = False + rpdat.rp_voxelgi_emission = False rpdat.rp_render_to_texture = True rpdat.rp_supersampling = '1' rpdat.rp_antialiasing = 'TAA' @@ -182,7 +186,7 @@ def update_preset(self, context): rpdat.rp_hdr = False rpdat.rp_background = 'Clear' rpdat.rp_stereo = False - rpdat.rp_voxelao = False + rpdat.rp_voxels = 'Off' rpdat.rp_render_to_texture = False rpdat.rp_supersampling = '1' rpdat.rp_antialiasing = 'Off' @@ -496,10 +500,32 @@ class ArmRPListItem(bpy.types.PropertyGroup): rp_dynres: BoolProperty(name="Dynamic Resolution", description="Dynamic resolution scaling for performance", default=False, update=update_renderpath) rp_chromatic_aberration: BoolProperty(name="Chromatic Aberration", description="Add chromatic aberration (scene fringe)", default=False, update=assets.invalidate_shader_cache) arm_ssr_half_res: BoolProperty(name="Half Res", description="Trace in half resolution", default=True, update=update_renderpath) - arm_voxelgi_dimensions: FloatProperty(name="Dimensions", description="Voxelization bounds",default=16, update=assets.invalidate_compiled_data) - arm_voxelgi_revoxelize: BoolProperty(name="Revoxelize", description="Revoxelize scene each frame", default=False, update=assets.invalidate_shader_cache) + rp_voxels: EnumProperty( + items=[('Off', 'Off', 'Off'), + ('Voxel GI', 'Voxel GI', 'Voxel GI'), + ('Voxel AO', 'Voxel AO', 'Voxel AO') + ], + name="Voxels", description="Dynamic global illumination", default='Off', update=update_renderpath) + rp_voxelgi_resolution: EnumProperty( + items=[('32', '32', '32'), + ('64', '64', '64'), + ('128', '128', '128'), + ('256', '256', '256'), + ], + name="Resolution", description="3D texture resolution", default='64', update=update_renderpath) + rp_voxelgi_resolution_z: EnumProperty( + items=[('1.0', '1.0', '1.0'), + ('0.5', '0.5', '0.5'), + ('0.25', '0.25', '0.25')], + name="Resolution Z", description="3D texture z resolution multiplier", default='1.0', update=update_renderpath) + arm_voxelgi_refraction: BoolProperty(name="Trace Refraction", description="Use voxels to render refraction", default=False, update=update_renderpath) + arm_voxelgi_bounces: EnumProperty( + items=[ + ('1', '1', '1'), + ('2', '2', '2')], + name="Bounces", description="Trace multiple light bounces", default='1', update=update_renderpath) + arm_voxelgi_clipmap_count: IntProperty(name="Clipmap count", description="Number of clipmaps", default=3, update=assets.invalidate_compiled_data) arm_voxelgi_temporal: BoolProperty(name="Temporal Filter", description="Use temporal filtering to stabilize voxels", default=False, update=assets.invalidate_shader_cache) - arm_voxelgi_camera: BoolProperty(name="Dynamic Camera", description="Use camera as voxelization origin", default=False, update=assets.invalidate_shader_cache) arm_voxelgi_shadows: BoolProperty(name="Shadows", description="Use voxels to render shadows", default=False, update=update_renderpath) arm_samples_per_pixel: EnumProperty( items=[('1', '1', '1'), @@ -516,11 +542,15 @@ class ArmRPListItem(bpy.types.PropertyGroup): ('1', '1', '1'), ], name="Cones", description="Number of cones to trace", default='5', update=assets.invalidate_shader_cache) + arm_voxelgi_diff: FloatProperty(name="Diffuse", description="", default=1.0, update=assets.invalidate_shader_cache) + arm_voxelgi_spec: FloatProperty(name="Reflection", description="", default=1.0, update=assets.invalidate_shader_cache) + arm_voxelgi_refr: FloatProperty(name="Refraction", description="", default=1.0, update=assets.invalidate_shader_cache) arm_voxelgi_occ: FloatProperty(name="Intensity", description="", default=1.0, update=assets.invalidate_shader_cache) + arm_voxelgi_size: FloatProperty(name="Size", description="Voxel size", default=0.25, update=assets.invalidate_shader_cache) arm_voxelgi_step: FloatProperty(name="Step", description="Step size", default=1.0, update=assets.invalidate_shader_cache) - arm_voxelgi_offset: FloatProperty(name="Offset", description="Ray offset", default=1.0, update=assets.invalidate_shader_cache) - arm_voxelgi_range: FloatProperty(name="Range", description="Maximum range", default=2.0, update=assets.invalidate_shader_cache) - arm_voxelgi_aperture: FloatProperty(name="Aperture", description="Cone aperture for shadow trace", default=1.0, update=assets.invalidate_shader_cache) + arm_voxelgi_range: FloatProperty(name="Range", description="Maximum range", default=1.0, update=assets.invalidate_shader_cache) + arm_voxelgi_offset: FloatProperty(name="Offset", description="Offset for dealing with self occlusion", default=1.0, update=assets.invalidate_shader_cache) + arm_voxelgi_aperture: FloatProperty(name="Aperture", description="Cone aperture for shadow trace", default=0.5, update=assets.invalidate_shader_cache) arm_sss_width: FloatProperty(name="Width", description="SSS blur strength", default=1.0, update=assets.invalidate_shader_cache) arm_water_color: FloatVectorProperty(name="Color", size=3, default=[1, 1, 1], subtype='COLOR', min=0, max=1, update=assets.invalidate_shader_cache) arm_water_level: FloatProperty(name="Level", default=0.0, update=assets.invalidate_shader_cache) diff --git a/blender/arm/props_ui.py b/blender/arm/props_ui.py index dc09f57693..ed5652daf9 100644 --- a/blender/arm/props_ui.py +++ b/blender/arm/props_ui.py @@ -1687,20 +1687,13 @@ def draw(self, context): row.label(text='Warning: Game will crash if texture size is higher than max texture size allowed by target.', icon='ERROR') class ARM_PT_RenderPathVoxelsPanel(bpy.types.Panel): - bl_label = "Voxel AO" + bl_label = "Voxels" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "render" bl_options = {'DEFAULT_CLOSED'} bl_parent_id = "ARM_PT_RenderPathPanel" - def draw_header(self, context): - wrd = bpy.data.worlds['Arm'] - if len(wrd.arm_rplist) <= wrd.arm_rplist_index: - return - rpdat = wrd.arm_rplist[wrd.arm_rplist_index] - self.layout.prop(rpdat, "rp_voxelao", text="") - def draw(self, context): layout = self.layout layout.use_property_split = True @@ -1710,22 +1703,35 @@ def draw(self, context): return rpdat = wrd.arm_rplist[wrd.arm_rplist_index] - layout.enabled = rpdat.rp_voxelao - layout.prop(rpdat, 'arm_voxelgi_shadows') - layout.prop(rpdat, 'arm_voxelgi_cones') - layout.prop(rpdat, 'rp_voxelgi_resolution') - layout.prop(rpdat, 'rp_voxelgi_resolution_z') - layout.prop(rpdat, 'arm_voxelgi_dimensions') - layout.prop(rpdat, 'arm_voxelgi_revoxelize') - col2 = layout.column() - col2.enabled = rpdat.arm_voxelgi_revoxelize - col2.prop(rpdat, 'arm_voxelgi_camera') - col2.prop(rpdat, 'arm_voxelgi_temporal') - layout.prop(rpdat, 'arm_voxelgi_occ') - layout.prop(rpdat, 'arm_voxelgi_step') - layout.prop(rpdat, 'arm_voxelgi_range') - layout.prop(rpdat, 'arm_voxelgi_offset') - layout.prop(rpdat, 'arm_voxelgi_aperture') + layout.prop(rpdat, 'rp_voxels') + col = layout.column() + col.enabled = rpdat.rp_voxels != 'Off' + col2 = col.column() + col2.enabled = rpdat.rp_voxels == 'Voxel GI' + col3 = col.column() + col3.enabled = rpdat.rp_voxels == 'Voxel AO' + col.prop(rpdat, 'arm_voxelgi_shadows', text='Shadows') + #col2.prop(rpdat, 'arm_voxelgi_refraction', text='Refraction') + #col2.prop(rpdat, 'arm_voxelgi_bounces') + col.prop(rpdat, 'arm_voxelgi_clipmap_count') + #col.prop(rpdat, 'arm_voxelgi_cones') + col.prop(rpdat, 'rp_voxelgi_resolution') + #col.prop(rpdat, 'rp_voxelgi_resolution_z') + col2.enabled = rpdat.rp_voxels == 'Voxel GI' + #col.prop(rpdat, 'arm_voxelgi_temporal') + col.label(text="Light") + col2 = col.column() + col2.enabled = rpdat.rp_voxels == 'Voxel GI' + col2.prop(rpdat, 'arm_voxelgi_diff') + col2.prop(rpdat, 'arm_voxelgi_spec') + #col2.prop(rpdat, 'arm_voxelgi_refr') + col.prop(rpdat, 'arm_voxelgi_occ') + col.label(text="Ray") + col.prop(rpdat, 'arm_voxelgi_size') + col.prop(rpdat, 'arm_voxelgi_step') + col.prop(rpdat, 'arm_voxelgi_range') + #col.prop(rpdat, 'arm_voxelgi_offset') + #col.prop(rpdat, 'arm_voxelgi_aperture') class ARM_PT_RenderPathWorldPanel(bpy.types.Panel): bl_label = "World" diff --git a/blender/arm/write_data.py b/blender/arm/write_data.py index 7c5327512d..c1123dc7bc 100644 --- a/blender/arm/write_data.py +++ b/blender/arm/write_data.py @@ -442,7 +442,7 @@ def write_config(resx, resy): 'rp_ss_refraction': rpdat.rp_ss_refraction != 'Off', 'rp_bloom': rpdat.rp_bloom != 'Off', 'rp_motionblur': rpdat.rp_motionblur != 'Off', - 'rp_gi': rpdat.rp_voxelao, + 'rp_gi': rpdat.rp_voxels != "Off", 'rp_dynres': rpdat.rp_dynres } @@ -473,10 +473,11 @@ class Main { public static inline var projectVersion = '""" + arm.utils.safestr(wrd.arm_project_version) + """'; public static inline var projectPackage = '""" + arm.utils.safestr(wrd.arm_project_package) + """';""") - if rpdat.rp_voxelao: + + if rpdat.rp_voxels == 'Voxel GI' or rpdat.rp_voxels == 'Voxel AO': f.write(""" - public static inline var voxelgiVoxelSize = """ + str(rpdat.arm_voxelgi_dimensions) + " / " + str(rpdat.rp_voxelgi_resolution) + """; - public static inline var voxelgiHalfExtents = """ + str(round(rpdat.arm_voxelgi_dimensions / 2.0)) + """;""") + public static inline var voxelgiClipmapCount = """ + str(rpdat.arm_voxelgi_clipmap_count) + """; + public static inline var voxelgiVoxelSize = """ + str(round(rpdat.arm_voxelgi_size * 100) / 100) + """;""") if rpdat.rp_bloom: f.write(f"public static var bloomRadius = {bpy.context.scene.eevee.bloom_radius if rpdat.arm_bloom_follow_blender else rpdat.arm_bloom_radius};") @@ -768,18 +769,22 @@ def write_compiledglsl(defs, make_variants): const float compoDOFLength = 160.0; """) # str(round(bpy.data.cameras[0].lens * 100) / 100) - if rpdat.rp_voxelao: - halfext = round(rpdat.arm_voxelgi_dimensions / 2.0) - f.write( -"""const ivec3 voxelgiResolution = ivec3(""" + str(rpdat.rp_voxelgi_resolution) + """, """ + str(rpdat.rp_voxelgi_resolution) + """, """ + str(int(int(rpdat.rp_voxelgi_resolution) * float(rpdat.rp_voxelgi_resolution_z))) + """); -const vec3 voxelgiHalfExtents = vec3(""" + str(halfext) + """, """ + str(halfext) + """, """ + str(round(halfext * float(rpdat.rp_voxelgi_resolution_z))) + """); + if rpdat.rp_voxels != 'Off': + f.write("""const ivec3 voxelgiResolution = ivec3(""" + str(rpdat.rp_voxelgi_resolution) + """, """ + str(rpdat.rp_voxelgi_resolution) + """, """ + str(rpdat.rp_voxelgi_resolution) + """); +const int voxelgiClipmapCount = """ + str(rpdat.arm_voxelgi_clipmap_count) + """; const float voxelgiOcc = """ + str(round(rpdat.arm_voxelgi_occ * 100) / 100) + """; +const float voxelgiVoxelSize = """ + str(round(rpdat.arm_voxelgi_size * 100) / 100) + """; const float voxelgiStep = """ + str(round(rpdat.arm_voxelgi_step * 100) / 100) + """; const float voxelgiRange = """ + str(round(rpdat.arm_voxelgi_range * 100) / 100) + """; const float voxelgiOffset = """ + str(round(rpdat.arm_voxelgi_offset * 100) / 100) + """; const float voxelgiAperture = """ + str(round(rpdat.arm_voxelgi_aperture * 100) / 100) + """; """) - + if rpdat.rp_voxels == 'Voxel GI': + f.write(""" +const float voxelgiDiff = """ + str(round(rpdat.arm_voxelgi_diff * 100) / 100) + """; +const float voxelgiRefl = """ + str(round(rpdat.arm_voxelgi_spec * 100) / 100) + """; +const float voxelgiRefr = """ + str(round(rpdat.arm_voxelgi_refr * 100) / 100) + """; +""") if rpdat.rp_sss: f.write(f"const float sssWidth = {rpdat.arm_sss_width / 10.0};\n")