diff --git a/bin/data/shaders/display/subpass.frag.glsl b/bin/data/shaders/display/subpass.frag.glsl index f8eefc4c..0286d1c6 100644 --- a/bin/data/shaders/display/subpass.frag.glsl +++ b/bin/data/shaders/display/subpass.frag.glsl @@ -13,6 +13,7 @@ struct Matrices { mat4 iView[2]; mat4 iProjection[2]; mat4 iProjectionView[2]; + mat4 ortho; }; struct Space { @@ -113,11 +114,8 @@ struct DrawCall { #endif layout (input_attachment_index = 3, binding = 3) uniform subpassInputMS samplerDepth; #endif -layout (binding = 4) uniform sampler3D samplerNoise; -layout (binding = 5) uniform samplerCube samplerSkybox; -layout (binding = 6) uniform sampler2D samplerTextures[TEXTURES]; -layout (binding = 7) uniform UBO { +layout (binding = 4) uniform UBO { Matrices matrices; Mode mode; @@ -137,19 +135,23 @@ layout (binding = 7) uniform UBO { uint padding2; } ubo; -layout (std140, binding = 8) readonly buffer Lights { +layout (std140, binding = 5) readonly buffer Lights { Light lights[]; }; -layout (std140, binding = 9) readonly buffer Materials { +layout (std140, binding = 6) readonly buffer Materials { Material materials[]; }; -layout (std140, binding = 10) readonly buffer Textures { +layout (std140, binding = 7) readonly buffer Textures { Texture textures[]; }; -layout (std140, binding = 11) readonly buffer DrawCalls { +layout (std140, binding = 8) readonly buffer DrawCalls { DrawCall drawCalls[]; }; +layout (binding = 9) uniform sampler3D samplerNoise; +layout (binding = 10) uniform samplerCube samplerSkybox; +layout (binding = 11) uniform sampler2D samplerTextures[TEXTURES]; + layout (location = 0) in vec2 inUv; layout (location = 1) in flat uint inPushConstantPass; @@ -208,7 +210,35 @@ void phong( Light light, vec4 albedoSpecular, inout vec3 i ) { i += Id + Is; } +void phongSpecular( Light light, vec4 albedoSpecular, inout vec3 i ) { + vec3 Ls = vec3(1.0, 1.0, 1.0); // light specular + vec3 Ld = light.color; // light color + vec3 La = vec3(1.0, 1.0, 1.0); + vec3 Ks = vec3(albedoSpecular.a); // material specular + vec3 Kd = albedoSpecular.rgb; // material diffuse + vec3 Ka = vec3(1.0, 1.0, 1.0); + + float Kexp = ubo.kexp; + + vec3 V = position.eye; + vec3 N = normal.eye; + vec3 L = light.position.xyz - V; + float dist = length(L); + + vec3 D = normalize(L); + vec3 R = reflect( -D, N ); + vec3 S = normalize(-V); + float s_factor = pow( max(dot( R, S ), 0.0), Kexp ); + if ( Kexp < 0.0001 ) s_factor = 0; + + float radiance = light.power / (dist * dist); + + vec3 Ia = La * Ka; + vec3 Is = Ls * Ks * s_factor * radiance; + + i += Is; +} const float PI = 3.14159265359; float DistributionGGX(vec3 N, vec3 H, float roughness) { float a = roughness*roughness; @@ -493,7 +523,38 @@ void pbr( Light light, vec3 albedo, float metallic, float roughness, vec3 lightP // add to outgoing radiance Lo i += (kD * albedo / PI + specular) * radiance * NdotL; } +void pbrSpecular( Light light, vec3 albedo, float metallic, float roughness, inout vec3 i ) { + vec3 F0 = vec3(0.04); + F0 = mix(F0, albedo, metallic); + vec3 N = normalize(normal.eye); + vec3 L = light.position.xyz - position.eye; + float dist = length(L); + + L = normalize(L); + vec3 V = normalize(-position.eye); + vec3 H = normalize(V + L); + + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.0); + float attenuation = light.power / (dist * dist); + vec3 radiance = light.color.rgb * attenuation; + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 kD = vec3(1.0) - F; + kD *= 1.0 - metallic; + + vec3 numerator = NDF * G * F; + float denominator = 4.0 * NdotV * NdotL; + vec3 specular = numerator / max(denominator, 0.001); + + // add to outgoing radiance Lo + i += specular * radiance * NdotL; +} vec2 rayBoxDst( vec3 boundsMin, vec3 boundsMax, vec3 rayO, vec3 rayD ) { vec3 t0 = (boundsMin - rayO) / rayD; vec3 t1 = (boundsMax - rayO) / rayD; @@ -558,13 +619,17 @@ void fog( vec3 rayO, vec3 rayD, inout vec3 i, float scale ) { } vec3 decodeNormals( vec2 enc ) { +#define kPI 3.1415926536f + vec2 ang = enc*2-1; + vec2 scth = vec2( sin(ang.x * kPI), cos(ang.x * kPI) ); + vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y); + return vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y); +/* vec2 fenc = enc*4-2; float f = dot(fenc,fenc); float g = sqrt(1-f/4); - vec3 n; - n.xy = fenc*g; - n.z = 1-f/2; - return normalize(n); + return normalize( vec3(fenc * g, 1 - f / 2) ); +*/ } float wrap( float i ) { return fract(i); @@ -637,12 +702,14 @@ void main() { rayD = normalize( far3 - near3 ); } #if !MULTISAMPLING - normal.eye = decodeNormals( subpassLoad(samplerNormal).xy ); + normal.world = decodeNormals( subpassLoad(samplerNormal).xy ); uvec2 ID = subpassLoad(samplerId).xy; #else - normal.eye = decodeNormals( resolve(samplerNormal).xy ); + normal.world = decodeNormals( resolve(samplerNormal).xy ); uvec2 ID = resolve(samplerId).xy; #endif + normal.eye = vec3( ubo.matrices.view[inPushConstantPass] * vec4(normal.world, 0.0) ); + uint drawId = ID.x; uint materialId = ID.y; if ( drawId == 0 || materialId == 0 ) { @@ -666,7 +733,8 @@ void main() { vec2 uv = resolve(samplerUv).xy; #endif bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas ); - Texture textureAtlas = ( useAtlas ) ? textures[drawCall.textureIndex + material.indexAtlas] : textures[0]; + Texture textureAtlas; + if ( useAtlas ) textureAtlas = textures[drawCall.textureIndex + material.indexAtlas]; if ( validTextureIndex( drawCall.textureIndex + material.indexAtlas ) ) { Texture t = textures[drawCall.textureIndex + material.indexAlbedo]; C = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip ); @@ -698,6 +766,20 @@ void main() { bool useLightmap = 0 <= material.indexLightmap; if ( useLightmap ) { fragColor = C.rgb + ubo.ambient.rgb; + for ( uint i = 0; i < ubo.lights; ++i ) { + Light light = lights[i]; + + if ( light.power <= 0.001 ) continue; + + light.position.xyz = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position.xyz, 1)); + if ( validTextureIndex(light.mapIndex) ) { + float factor = shadowFactor( light, light.mapIndex, 0.0 ); + light.power *= factor; + litFactor += light.power; + } + if ( light.power <= 0.0001 ) continue; + if ( usePbr ) pbrSpecular( light, C.rgb, M, R, fragColor ); else phongSpecular( light, C, fragColor ); + } /* float totalShadows = 0.0; float totalLights = 0.0; diff --git a/bin/data/shaders/display/subpass.svogi.frag.glsl b/bin/data/shaders/display/subpass.svogi.frag.glsl new file mode 100644 index 00000000..110d1466 --- /dev/null +++ b/bin/data/shaders/display/subpass.svogi.frag.glsl @@ -0,0 +1,687 @@ +#version 450 +#extension GL_EXT_samplerless_texture_functions : require + +#define MULTISAMPLING 1 +#define RAY_MARCH_FOG 1 +#define UF_DEFERRED_SAMPLING 0 + +layout (constant_id = 0) const uint TEXTURES = 256; + +struct Matrices { + mat4 view[2]; + mat4 projection[2]; + mat4 iView[2]; + mat4 iProjection[2]; + mat4 iProjectionView[2]; + mat4 voxel; +}; + +struct Space { + vec3 eye; + vec3 world; +} position, normal, view; + +struct Fog { + vec3 color; + float stepScale; + + vec3 offset; + float densityScale; + + float densityThreshold; + float densityMultiplier; + float absorbtion; + float padding1; + + vec2 range; + float padding2; + float padding3; +}; + +struct Mode { + uint type; + uint scalar; + vec2 padding; + vec4 parameters; +}; + +struct Light { + vec3 position; + float radius; + + vec3 color; + float power; + + int type; + int mapIndex; + float depthBias; + float padding; + + mat4 view; + mat4 projection; +}; + +struct Material { + vec4 colorBase; + vec4 colorEmissive; + + float factorMetallic; + float factorRoughness; + float factorOcclusion; + float factorAlphaCutoff; + + int indexAlbedo; + int indexNormal; + int indexEmissive; + int indexOcclusion; + + int indexMetallicRoughness; + int indexAtlas; + int indexLightmap; + int modeAlpha; +}; +struct Texture { + int index; + int samp; + int remap; + float blend; + + vec4 lerp; +}; +struct DrawCall { + int materialIndex; + uint materials; + int textureIndex; + uint textures; +}; +struct Voxel { + uvec2 id; + vec3 position; + vec3 normal; + vec2 uv; + vec4 color; +}; + +#if !MULTISAMPLING + layout (input_attachment_index = 0, binding = 0) uniform usubpassInput samplerId; + layout (input_attachment_index = 1, binding = 1) uniform subpassInput samplerNormal; + #if UF_DEFERRED_SAMPLING + layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerUv; + #else + layout (input_attachment_index = 2, binding = 2) uniform subpassInput samplerAlbedo; + #endif + layout (input_attachment_index = 3, binding = 3) uniform subpassInput samplerDepth; +#else + layout (input_attachment_index = 0, binding = 0) uniform usubpassInputMS samplerId; + layout (input_attachment_index = 1, binding = 1) uniform subpassInputMS samplerNormal; + #if UF_DEFERRED_SAMPLING + layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerUv; + #else + layout (input_attachment_index = 2, binding = 2) uniform subpassInputMS samplerAlbedo; + #endif + layout (input_attachment_index = 3, binding = 3) uniform subpassInputMS samplerDepth; +#endif + +layout (binding = 4) uniform UBO { + Matrices matrices; + + Mode mode; + Fog fog; + + uint lights; + uint materials; + uint textures; + uint drawCalls; + + vec3 ambient; + float kexp; + + uint msaa; + uint poissonSamples; + uint padding1; + uint padding2; +} ubo; + +layout (std140, binding = 5) readonly buffer Lights { + Light lights[]; +}; +layout (std140, binding = 6) readonly buffer Materials { + Material materials[]; +}; +layout (std140, binding = 7) readonly buffer Textures { + Texture textures[]; +}; +layout (std140, binding = 8) readonly buffer DrawCalls { + DrawCall drawCalls[]; +}; + +layout (binding = 9) uniform usampler3D voxelId; +layout (binding = 10) uniform sampler3D voxelNormal; +layout (binding = 11) uniform sampler3D voxelUv; +layout (binding = 12) uniform sampler3D voxelAlbedo; + +layout (binding = 13) uniform sampler3D samplerNoise; +layout (binding = 14) uniform samplerCube samplerSkybox; +layout (binding = 15) uniform sampler2D samplerTextures[TEXTURES]; + +layout (location = 0) in vec2 inUv; +layout (location = 1) in flat uint inPushConstantPass; + +layout (location = 0) out vec4 outFragColor; +layout (location = 1) out vec4 outDebugValue; + +vec2 poissonDisk[16] = vec2[]( + vec2( -0.94201624, -0.39906216 ), + vec2( 0.94558609, -0.76890725 ), + vec2( -0.094184101, -0.92938870 ), + vec2( 0.34495938, 0.29387760 ), + vec2( -0.91588581, 0.45771432 ), + vec2( -0.81544232, -0.87912464 ), + vec2( -0.38277543, 0.27676845 ), + vec2( 0.97484398, 0.75648379 ), + vec2( 0.44323325, -0.97511554 ), + vec2( 0.53742981, -0.47373420 ), + vec2( -0.26496911, -0.41893023 ), + vec2( 0.79197514, 0.19090188 ), + vec2( -0.24188840, 0.99706507 ), + vec2( -0.81409955, 0.91437590 ), + vec2( 0.19984126, 0.78641367 ), + vec2( 0.14383161, -0.14100790 ) +); + +const float PI = 3.14159265359; +const float EPSILON = 0.00001; + +// GGX/Towbridge-Reitz normal distribution function. +// Uses Disney's reparametrization of alpha = roughness^2. +float ndfGGX(float cosLh, float roughness) { + float alpha = roughness * roughness; + float alphaSq = alpha * alpha; + + float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0; + return alphaSq / (PI * denom * denom); +} + +// Single term for separable Schlick-GGX below. +float gaSchlickG1(float cosTheta, float k) { + return cosTheta / (cosTheta * (1.0 - k) + k); +} + +// Schlick-GGX approximation of geometric attenuation function using Smith's method. +float gaSchlickGGX(float cosLi, float cosLo, float roughness) { + float r = roughness + 1.0; + float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights. + return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k); +} +vec3 fresnelSchlick(vec3 F0, float cosTheta) { + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} + +float random(vec3 seed, int i){ + vec4 seed4 = vec4(seed,i); + float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673)); + return fract(sin(dot_product) * 43758.5453); +} + +float shadowFactor( Light light, uint shadowMap, float def ) { + vec4 positionClip = light.projection * light.view * vec4(position.world, 1.0); + positionClip.xyz /= positionClip.w; + + if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0; + if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0; + if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0; + + float factor = 1.0; + + // spot light + if ( light.type == 1 || light.type == 2 ) { + float dist = length( positionClip.xy ); + if ( dist > 0.5 ) return def; //0.0; + + // spot light with attenuation + if ( light.type == 2 ) { + factor = 1.0 - (pow(dist * 2,2.0)); + } + } + + vec2 uv = positionClip.xy * 0.5 + 0.5; + float bias = light.depthBias; + + float eyeDepth = positionClip.z; + + int samples = int(ubo.poissonSamples); + if ( samples <= 1 ) { + return eyeDepth < texture(samplerTextures[shadowMap], uv).r - bias ? 0.0 : factor; + } + for ( int i = 0; i < samples; ++i ) { + // int index = i; + // int index = int( float(samples) * random(gl_FragCoord.xyy, i) ) % samples; + int index = int( float(samples) * random(floor(position.world.xyz * 1000.0), i)) % samples; + float lightDepth = texture(samplerTextures[shadowMap], uv + poissonDisk[index] / 700.0 ).r; + if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples; + } + return factor; +} +// Returns a vector that is orthogonal to u. +vec3 orthogonal(vec3 u){ + u = normalize(u); + vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector. + return abs(dot(u, v)) > 0.99999f ? cross(u, vec3(0, 1, 0)) : cross(u, v); +} +float rand2(vec2 co){ + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 143758.5453); +} +float rand3(vec3 co){ + return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 37.719))) * 143758.5453); +} +void whitenoise(inout vec3 color) { + float flicker = ubo.mode.parameters.x; + float pieces = ubo.mode.parameters.y; + float blend = ubo.mode.parameters.z; + float time = ubo.mode.parameters.w; + if ( blend < 0.0001 ) return; + float freq = sin(pow(mod(time, flicker) + flicker, 1.9)); +// float whiteNoise = rand3( floor(position.world / pieces) + floor(time * 2) ); + float whiteNoise = rand2( floor(gl_FragCoord.xy / pieces) + mod(time, freq) ); + color = mix( color, vec3(whiteNoise), blend ); +} +vec3 gamma( vec3 i ) { + return pow(i.rgb, vec3(1.0 / 2.2)); +} + +vec2 rayBoxDst( vec3 boundsMin, vec3 boundsMax, vec3 rayO, vec3 rayD ) { + vec3 t0 = (boundsMin - rayO) / rayD; + vec3 t1 = (boundsMax - rayO) / rayD; + vec3 tmin = min(t0, t1); + vec3 tmax = max(t0, t1); + float dstA = max( max(tmin.x, tmin.y), tmin.z ); + float dstB = min( tmax.x, min(tmax.y, tmax.z) ); + float tStart = max(0, dstA); + float tEnd = max(0, dstB - tStart); + return vec2(tStart, tEnd); +} + +float sampleDensity( vec3 position ) { + vec3 uvw = position * ubo.fog.densityScale * 0.001 + ubo.fog.offset * 0.01; + return max(0, texture(samplerNoise, uvw).r - ubo.fog.densityThreshold) * ubo.fog.densityMultiplier; +} + +void fog( vec3 rayO, vec3 rayD, inout vec3 i, float scale ) { + if ( ubo.fog.stepScale <= 0 ) return; + if ( ubo.fog.range.x == 0 || ubo.fog.range.y == 0 ) return; + +#if RAY_MARCH_FOG + float range = ubo.fog.range.y; + vec3 boundsMin = vec3(-range,-range,-range) + rayO; + vec3 boundsMax = vec3(range,range,range) + rayO; + int numSteps = int(length(boundsMax - boundsMin) * ubo.fog.stepScale ); + + vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, rayO, rayD ); + float dstToBox = rayBoxInfo.x; + float dstInsideBox = rayBoxInfo.y; + float depth = position.eye.z; + + float lightEnergy = 0; + // march + if ( 0 <= dstInsideBox && dstToBox <= depth ) { + float dstTravelled = 0; + float stepSize = dstInsideBox / numSteps; + float dstLimit = min( depth - dstToBox, dstInsideBox ); + float totalDensity = 0; + float transmittance = 1; + while ( dstTravelled < dstLimit ) { + vec3 rayPos = rayO + rayD * (dstToBox + dstTravelled); + float density = sampleDensity(rayPos); + if ( density > 0 ) { + transmittance *= exp(-density * stepSize * ubo.fog.absorbtion); + if ( transmittance < 0.01 ) break; + } + dstTravelled += stepSize; + } + i.rgb = mix(ubo.fog.color.rgb, i.rgb, transmittance); + } +#endif + + vec3 color = ubo.fog.color.rgb; + float inner = ubo.fog.range.x; + float outer = ubo.fog.range.y * scale; + float distance = length(-position.eye); + float factor = (distance - inner) / (outer - inner); + factor = clamp( factor, 0.0, 1.0 ); + + i.rgb = mix(i.rgb, color, factor); +} + +vec3 decodeNormals( vec2 enc ) { +#define kPI 3.1415926536f + vec2 ang = enc*2-1; + vec2 scth = vec2( sin(ang.x * kPI), cos(ang.x * kPI) ); + vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y); + return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) ); +/* + vec2 fenc = enc*4-2; + float f = dot(fenc,fenc); + float g = sqrt(1-f/4); + return normalize( vec3(fenc * g, 1 - f / 2) ); +*/ +} +float wrap( float i ) { + return fract(i); +} +vec2 wrap( vec2 uv ) { + return vec2( wrap( uv.x ), wrap( uv.y ) ); +} +float mipLevel( in vec2 uv ) { + vec2 dx_vtc = dFdx(uv); + vec2 dy_vtc = dFdy(uv); + return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc))); +} +bool validTextureIndex( int textureIndex ) { + return 0 <= textureIndex; // && textureIndex < ubo.textures; +} +vec4 resolve( subpassInputMS t ) { + int samples = int(ubo.msaa); + vec4 resolved = vec4(0); + for ( int i = 0; i < samples; ++i ) { + resolved += subpassLoad(t, i); + } + resolved /= vec4(samples); + return resolved; +} +uvec4 resolve( usubpassInputMS t ) { + int samples = int(ubo.msaa); + uvec4 resolved = uvec4(0); + for ( int i = 0; i < samples; ++i ) { + resolved += subpassLoad(t, i); + } + resolved /= uvec4(samples); + return resolved; +} + +Voxel getVoxel( vec3 P ) { + vec3 uvw = vec3( ubo.matrices.voxel * vec4( P, 1.0f ) ) * 0.5f + 0.5f; + + Voxel voxel; + voxel.id = uvec2(texture(voxelId, uvw).xy); + voxel.position = P; + voxel.normal = decodeNormals( texture(voxelNormal, uvw).xy ); + voxel.uv = texture(voxelUv, uvw).xy; + voxel.color = texture(voxelAlbedo, uvw).rgba; + + return voxel; +} + +#define VOXEL_TRACE_IN_NDC 0 +vec4 voxelConeTrace( vec3 rayO, vec3 rayD, float aperture ) { + // bounds + float albedoSize = textureSize( voxelAlbedo, 0 ).x; + float mipmapLevels = textureQueryLod( voxelAlbedo, vec3(0) ).x; +#if VOXEL_TRACE_IN_NDC + rayO = vec3( ubo.matrices.voxel * vec4( rayO, 1.0 ) ); + rayD = vec3( ubo.matrices.voxel * vec4( rayD, 0.0 ) ); + + vec3 boundsMin = vec3( -1 ); + vec3 boundsMax = vec3( 1 ); + float voxelSize = 1.0 / albedoSize; +#else + mat4 inverseOrtho = inverse( ubo.matrices.voxel ); + vec3 boundsMin = vec3( inverseOrtho * vec4( -1, -1, -1, 1 ) ); + vec3 boundsMax = vec3( inverseOrtho * vec4( 1, 1, 1, 1 ) ); + float voxelSize = 1; +#endif + float granularity = 1.0 / 6.0; // (2.0 * sqrt(2.0) ); + // box + vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, rayO, rayD ); + float tStart = rayBoxInfo.x; + float tEnd = rayBoxInfo.y; + // steps + float tDelta = voxelSize * granularity; + uint maxSteps = uint(albedoSize / granularity); + // marcher + float t = tStart + tDelta * 2.0; + vec3 rayPos = vec3(0); + vec4 radiance = vec4(0); + vec3 uvw = vec3(0); + // cone mipmap shit + float coneCoefficient = 2.0 * tan(aperture * 0.5); + float coneDiameter = coneCoefficient * t; + float level = aperture > 0 ? log2( coneDiameter / albedoSize ) : 0; + // results + vec4 color = vec4(0); + float occlusion = 0.0; + // do + uint stepCounter = 0; + while ( t < tEnd && occlusion < 1.0 && stepCounter++ < maxSteps ) { + t += tDelta; + rayPos = rayO + rayD * t; + #if VOXEL_TRACE_IN_NDC + uvw = rayPos * 0.5 + 0.5; + #else + uvw = (rayPos - boundsMin) / (boundsMax - boundsMin); // vec3( ubo.matrices.voxel * vec4( rayPos, 1 ) ) * 0.5 + 0.5; + #endif + if ( abs(uvw.x) > 1.0 || abs(uvw.y) > 1.0 || abs(uvw.z) > 1.0 ) break; + coneDiameter = coneCoefficient * t; + level = log2( coneDiameter / albedoSize ); + radiance = texture(voxelAlbedo, uvw, level) * granularity; + + occlusion += radiance.a; + color.rgb += (1.0 - occlusion) * radiance.rgb; + } + return vec4(color.rgb, occlusion); +} + +void main() { + vec3 rayO = vec3(0); + vec3 rayD = vec3(0); + vec3 fragColor = vec3(0); + + { + #if !MULTISAMPLING + float depth = subpassLoad(samplerDepth).r; + #else + float depth = resolve( samplerDepth ).r; + #endif + + vec4 positionClip = vec4(inUv * 2.0 - 1.0, depth, 1.0); + vec4 positionEye = ubo.matrices.iProjection[inPushConstantPass] * positionClip; + positionEye /= positionEye.w; + position.eye = positionEye.xyz; + + vec4 positionWorld = ubo.matrices.iView[inPushConstantPass] * positionEye; + position.world = positionWorld.xyz; + } + { + vec4 near4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, -1.0, 1.0)); + vec4 far4 = ubo.matrices.iProjectionView[inPushConstantPass] * (vec4(2.0 * inUv - 1.0, 1.0, 1.0)); + vec3 near3 = near4.xyz / near4.w; + vec3 far3 = far4.xyz / far4.w; + + rayO = near3; + } + { + mat4 iProjectionView = inverse( ubo.matrices.projection[inPushConstantPass] * mat4(mat3(ubo.matrices.view[inPushConstantPass])) ); + vec4 near4 = iProjectionView * (vec4(2.0 * inUv - 1.0, -1.0, 1.0)); + vec4 far4 = iProjectionView * (vec4(2.0 * inUv - 1.0, 1.0, 1.0)); + vec3 near3 = near4.xyz / near4.w; + vec3 far3 = far4.xyz / far4.w; + + rayD = normalize( far3 - near3 ); + } +#if !MULTISAMPLING + normal.world = decodeNormals( subpassLoad(samplerNormal).xy ); + uvec2 ID = subpassLoad(samplerId).xy; +#else + normal.world = decodeNormals( resolve(samplerNormal).xy ); + uvec2 ID = resolve(samplerId).xy; +#endif + normal.eye = vec3( ubo.matrices.view[inPushConstantPass] * vec4(normal.world, 0.0) ); + + uint drawId = ID.x; + uint materialId = ID.y; + if ( drawId == 0 || materialId == 0 ) { + fragColor.rgb = texture( samplerSkybox, rayD ).rgb; + fog(rayO, rayD, fragColor, 0.0); + outFragColor = vec4(fragColor,1); + return; + } + --drawId; + --materialId; + + DrawCall drawCall = drawCalls[drawId]; + materialId += drawCall.materialIndex; + + Material material = materials[materialId]; + vec4 C = material.colorBase; +#if UF_DEFERRED_SAMPLING +#if !MULTISAMPLING + vec2 uv = subpassLoad(samplerUv).xy; +#else + vec2 uv = resolve(samplerUv).xy; +#endif + bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas ); + Texture textureAtlas; + if ( useAtlas ) textureAtlas = textures[drawCall.textureIndex + material.indexAtlas]; + if ( validTextureIndex( drawCall.textureIndex + material.indexAtlas ) ) { + Texture t = textures[drawCall.textureIndex + material.indexAlbedo]; + C = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip ); + } + // OPAQUE + if ( material.modeAlpha == 0 ) { + C.a = 1; + // BLEND + } else if ( material.modeAlpha == 1 ) { + + // MASK + } else if ( material.modeAlpha == 2 ) { + + } +#else +#if !MULTISAMPLING + C = subpassLoad(samplerAlbedo); +#else + C = resolve(samplerAlbedo); +#endif +#endif + float M = material.factorMetallic; + float R = material.factorRoughness; + float AO = material.factorOcclusion; + + vec4 indirectLighting = vec4(0); + // GI + { + vec3 P = position.world; + vec3 N = normal.world; + + // outFragColor = voxelConeTrace(rayO, rayD, 0 ); + // return; + + const float ROUGHNESS_SCALE = clamp(sqrt( 0.111111f / ( R * R + 0.111111f ) ), 0.0, 1.0); + const float ANGLE_MIX = 0.5f; + const float DIFFUSE_CONE_APERTURE = 0.325; + const float DIFFUSE_INDIRECT_FACTOR = ROUGHNESS_SCALE; //0.11111f * 0.111111f; + + const vec3 ortho = normalize(orthogonal(N)); + const vec3 ortho2 = normalize(cross(ortho, N)); + const vec3 corner = 0.5f * (ortho + ortho2); + const vec3 corner2 = 0.5f * (ortho - ortho2); + + const vec3 CONES[9] = { + N, + + mix(N, ortho, ANGLE_MIX), + mix(N, -ortho, ANGLE_MIX), + mix(N, ortho2, ANGLE_MIX), + mix(N, -ortho2, ANGLE_MIX), + + mix(N, corner, ANGLE_MIX), + mix(N, -corner, ANGLE_MIX), + mix(N, corner2, ANGLE_MIX), + mix(N, -corner2, ANGLE_MIX), + }; + + vec4 indirectDiffuse = vec4(0); + vec4 indirectSpecular = vec4(0); + + // indirectDiffuse += voxelConeTrace( P, N, 0.325 ); + for ( uint i = 0; i < 1; ++i ) { + indirectDiffuse += voxelConeTrace(P, CONES[i], DIFFUSE_CONE_APERTURE ); + } + + const float SPECULAR_CONE_APERTURE = acos( ROUGHNESS_SCALE ); + const float SPECULAR_INDIRECT_FACTOR = ROUGHNESS_SCALE; // 0.1; + + vec3 R = reflect( normalize(P - rayO), N ); + indirectSpecular = voxelConeTrace( P, R, SPECULAR_CONE_APERTURE ); + indirectLighting = indirectDiffuse * DIFFUSE_INDIRECT_FACTOR + indirectSpecular * SPECULAR_INDIRECT_FACTOR; + // AO = 1.0 - clamp(indirectDiffuse.a, 0.0, 1.0); + // outFragColor.rgb = vec3(AO); + // return; + + // outFragColor.rgb = indirectLighting.rgb; + // outFragColor.rgb = indirectDiffuse.rgb; + // outFragColor.rgb = indirectSpecular.rgb; + // return; + } + R *= 2.0f; + + bool usePbr = true; + bool gammaCorrect = false; + float litFactor = 1.0; + bool useLightmap = 0 <= material.indexLightmap; + if ( useLightmap ) { + fragColor = C.rgb + ubo.ambient.rgb + indirectLighting.rgb; + } else { + fragColor = C.rgb * ubo.ambient.rgb * (1 - AO) + indirectLighting.rgb; + } + { + const float LIGHT_POWER_CUTOFF = 0.0001; + vec3 N = normal.eye; + vec3 F0 = mix(vec3(0.04), C.rgb, M); + for ( uint i = 0; i < ubo.lights; ++i ) { + Light light = lights[i]; + if ( light.power <= LIGHT_POWER_CUTOFF ) continue; + light.position = vec3(ubo.matrices.view[inPushConstantPass] * vec4(light.position.xyz, 1)); + if ( validTextureIndex(light.mapIndex) ) { + float factor = shadowFactor( light, light.mapIndex, 0.0 ); + light.power *= factor; + litFactor += light.power; + } + vec3 Li = light.position - position.eye; + light.power *= 1.0 / (PI * pow(length(Li), 2.0)); + if ( light.power <= LIGHT_POWER_CUTOFF ) continue; + + Li = normalize(Li); + vec3 Lo = normalize( -position.eye ); + vec3 Lh = normalize(Li + Lo); + + vec3 Lradiance = light.color.rgb * light.power; + vec3 albedo = C.rgb; + + float cosLi = max(0.0, dot(N, Li)); + float cosLo = max(0.0, dot(N, Lo)); + float cosLh = max(0.0, dot(N, Lh)); + + vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) ); + float D = ndfGGX( cosLh, R ); + float G = gaSchlickGGX(cosLi, cosLo, R); + + vec3 Kd = mix( vec3(1.0) - F, vec3(0.0), M ); + vec3 diffuseBRDF = Kd * albedo; + vec3 specularBRDF = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo); + if ( useLightmap ) { + fragColor.rgb += (specularBRDF) * Lradiance * cosLi; + } else { + fragColor.rgb += (diffuseBRDF + specularBRDF) * Lradiance * cosLi; + } + } + } + if ( gammaCorrect ) fragColor = gamma( fragColor ); + + fog(rayO, rayD, fragColor, litFactor); + + if ( (ubo.mode.type & (0x1 << 1)) == (0x1 << 1) ) { + whitenoise(fragColor); + } + outFragColor = vec4(fragColor,1); + +} \ No newline at end of file diff --git a/bin/data/shaders/display/svogi.comp.glsl b/bin/data/shaders/display/svogi.comp.glsl new file mode 100644 index 00000000..a57f83fa --- /dev/null +++ b/bin/data/shaders/display/svogi.comp.glsl @@ -0,0 +1,371 @@ +#version 450 +#extension GL_EXT_samplerless_texture_functions : require + +layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in; + +#define MULTISAMPLING 1 +#define RAY_MARCH_FOG 1 +#define UF_DEFERRED_SAMPLING 0 + +layout (constant_id = 0) const uint TEXTURES = 256; + +struct Matrices { + mat4 view[2]; + mat4 projection[2]; + mat4 iView[2]; + mat4 iProjection[2]; + mat4 iProjectionView[2]; + mat4 voxel; +}; + +struct Space { + vec3 eye; + vec3 world; +} position, normal, view; + +struct Fog { + vec3 color; + float stepScale; + + vec3 offset; + float densityScale; + + float densityThreshold; + float densityMultiplier; + float absorbtion; + float padding1; + + vec2 range; + float padding2; + float padding3; +}; + +struct Mode { + uint type; + uint scalar; + vec2 padding; + vec4 parameters; +}; + +struct Light { + vec3 position; + float radius; + + vec3 color; + float power; + + int type; + int mapIndex; + float depthBias; + float padding; + + mat4 view; + mat4 projection; +}; + +struct Material { + vec4 colorBase; + vec4 colorEmissive; + + float factorMetallic; + float factorRoughness; + float factorOcclusion; + float factorAlphaCutoff; + + int indexAlbedo; + int indexNormal; + int indexEmissive; + int indexOcclusion; + + int indexMetallicRoughness; + int indexAtlas; + int indexLightmap; + int modeAlpha; +}; +struct Texture { + int index; + int samp; + int remap; + float blend; + + vec4 lerp; +}; +struct DrawCall { + int materialIndex; + uint materials; + int textureIndex; + uint textures; +}; + +layout (binding = 4) uniform UBO { + Matrices matrices; + + Mode mode; + Fog fog; + + uint lights; + uint materials; + uint textures; + uint drawCalls; + + vec3 ambient; + float kexp; + + uint msaa; + uint poissonSamples; + uint padding1; + uint padding2; +} ubo; + +layout (std140, binding = 5) readonly buffer Lights { + Light lights[]; +}; +layout (std140, binding = 6) readonly buffer Materials { + Material materials[]; +}; +layout (std140, binding = 7) readonly buffer Textures { + Texture textures[]; +}; +layout (std140, binding = 8) readonly buffer DrawCalls { + DrawCall drawCalls[]; +}; + +layout (binding = 9, rg16ui) uniform volatile coherent uimage3D voxelID; +layout (binding = 10, rg16f) uniform volatile coherent image3D voxelNormal; +layout (binding = 11, rg16f) uniform volatile coherent image3D voxelUv; +layout (binding = 12, rgba8) uniform volatile coherent image3D voxelAlbedo; + +layout (binding = 13) uniform sampler3D samplerNoise; +layout (binding = 14) uniform samplerCube samplerSkybox; +layout (binding = 15) uniform sampler2D samplerTextures[TEXTURES]; + +const float PI = 3.14159265359; +const float EPSILON = 0.00001; + +// GGX/Towbridge-Reitz normal distribution function. +// Uses Disney's reparametrization of alpha = roughness^2. +float ndfGGX(float cosLh, float roughness) { + float alpha = roughness * roughness; + float alphaSq = alpha * alpha; + + float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0; + return alphaSq / (PI * denom * denom); +} + +// Single term for separable Schlick-GGX below. +float gaSchlickG1(float cosTheta, float k) { + return cosTheta / (cosTheta * (1.0 - k) + k); +} + +// Schlick-GGX approximation of geometric attenuation function using Smith's method. +float gaSchlickGGX(float cosLi, float cosLo, float roughness) { + float r = roughness + 1.0; + float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights. + return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k); +} +vec3 fresnelSchlick(vec3 F0, float cosTheta) { + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} + +float random(vec3 seed, int i){ + vec4 seed4 = vec4(seed,i); + float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673)); + return fract(sin(dot_product) * 43758.5453); +} + +vec2 poissonDisk[16] = vec2[]( + vec2( -0.94201624, -0.39906216 ), + vec2( 0.94558609, -0.76890725 ), + vec2( -0.094184101, -0.92938870 ), + vec2( 0.34495938, 0.29387760 ), + vec2( -0.91588581, 0.45771432 ), + vec2( -0.81544232, -0.87912464 ), + vec2( -0.38277543, 0.27676845 ), + vec2( 0.97484398, 0.75648379 ), + vec2( 0.44323325, -0.97511554 ), + vec2( 0.53742981, -0.47373420 ), + vec2( -0.26496911, -0.41893023 ), + vec2( 0.79197514, 0.19090188 ), + vec2( -0.24188840, 0.99706507 ), + vec2( -0.81409955, 0.91437590 ), + vec2( 0.19984126, 0.78641367 ), + vec2( 0.14383161, -0.14100790 ) +); + +float shadowFactor( Light light, uint shadowMap, float def ) { + vec4 positionClip = light.projection * light.view * vec4(position.world, 1.0); + positionClip.xyz /= positionClip.w; + + if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; //0.0; + if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; //0.0; + if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; //0.0; + + float factor = 1.0; + + // spot light + if ( light.type == 1 || light.type == 2 ) { + float dist = length( positionClip.xy ); + if ( dist > 0.5 ) return def; //0.0; + + // spot light with attenuation + if ( light.type == 2 ) { + factor = 1.0 - (pow(dist * 2,2.0)); + } + } + + vec2 uv = positionClip.xy * 0.5 + 0.5; + float bias = light.depthBias; +/* + if ( true ) { + float cosTheta = clamp(dot(normal.eye, normalize(light.position.xyz - position.eye)), 0, 1); + bias = clamp(bias * tan(acos(cosTheta)), 0, 0.01); + } else if ( true ) { + bias = max(bias * 10 * (1.0 - dot(normal.eye, normalize(light.position.xyz - position.eye))), bias); + } +*/ + + float eyeDepth = positionClip.z; + + int samples = int(ubo.poissonSamples); + if ( samples <= 1 ) { + return eyeDepth < texture(samplerTextures[shadowMap], uv).r - bias ? 0.0 : factor; + } + for ( int i = 0; i < samples; ++i ) { + // int index = i; + // int index = int( float(samples) * random(gl_FragCoord.xyy, i) ) % samples; + int index = int( float(samples) * random(floor(position.world.xyz * 1000.0), i)) % samples; + float lightDepth = texture(samplerTextures[shadowMap], uv + poissonDisk[index] / 700.0 ).r; + if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples; + } + return factor; +} + +vec3 decodeNormals( vec2 enc ) { +#define kPI 3.1415926536f + vec2 ang = enc*2-1; + vec2 scth = vec2( sin(ang.x * kPI), cos(ang.x * kPI) ); + vec2 scphi = vec2(sqrt(1.0 - ang.y*ang.y), ang.y); + return normalize( vec3(scth.y*scphi.x, scth.x*scphi.x, scphi.y) ); +/* + vec2 fenc = enc*4-2; + float f = dot(fenc,fenc); + float g = sqrt(1-f/4); + return normalize( vec3(fenc * g, 1 - f / 2) ); +*/ +} +float wrap( float i ) { + return fract(i); +} +vec2 wrap( vec2 uv ) { + return vec2( wrap( uv.x ), wrap( uv.y ) ); +} + +bool validTextureIndex( int textureIndex ) { + return 0 <= textureIndex; // && textureIndex < ubo.textures; +} + +void main() { + vec3 tUvw = gl_GlobalInvocationID.xyz; + + uvec2 ID = uvec2(imageLoad(voxelID, ivec3(tUvw) ).xy); + vec2 N = vec2(imageLoad(voxelNormal, ivec3(tUvw) ).xy); + + normal.world = decodeNormals( N ); + normal.eye = vec3( ubo.matrices.voxel * vec4( normal.world, 0.0f ) ); + + position.eye = vec3(gl_GlobalInvocationID.xyz) / vec3(imageSize(voxelAlbedo)) * 2.0f - 1.0f; + position.world = vec3( inverse(ubo.matrices.voxel) * vec4( position.eye, 1.0f ) ); + + vec2 uv = imageLoad(voxelUv, ivec3(tUvw) ).xy; + + uint drawId = ID.x; + uint materialId = ID.y; + if ( drawId == 0 || materialId == 0 ) { + imageStore(voxelAlbedo, ivec3(tUvw), vec4(0)); + return; + } + --drawId; + --materialId; + + DrawCall drawCall = drawCalls[drawId]; + materialId += drawCall.materialIndex; + + Material material = materials[materialId]; + vec4 C = material.colorBase; + + bool useAtlas = validTextureIndex( drawCall.textureIndex + material.indexAtlas ); + Texture textureAtlas; + if ( useAtlas ) textureAtlas = textures[drawCall.textureIndex + material.indexAtlas]; + if ( validTextureIndex( drawCall.textureIndex + material.indexAtlas ) ) { + Texture t = textures[drawCall.textureIndex + material.indexAlbedo]; + C = texture( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv ); + } + + // OPAQUE + if ( material.modeAlpha == 0 ) { + C.a = 1; + // BLEND + } else if ( material.modeAlpha == 1 ) { + + // MASK + } else if ( material.modeAlpha == 2 ) { + + } + + float M = material.factorMetallic; + float R = material.factorRoughness * 4.0; + float AO = material.factorOcclusion; + + bool usePbr = true; + bool gammaCorrect = false; + float litFactor = 1.0; + bool useLightmap = 0 <= material.indexLightmap; + vec3 fragColor = vec3(0.0); + if ( useLightmap ) { + fragColor = C.rgb + ubo.ambient.rgb; + } else { + fragColor = C.rgb * ubo.ambient.rgb * (1 - AO); + } + { + const float LIGHT_POWER_CUTOFF = 0.005; + vec3 N = normal.world; + vec3 F0 = mix(vec3(0.04), C.rgb, M); + for ( uint i = 0; i < ubo.lights; ++i ) { + Light light = lights[i]; + if ( light.power <= LIGHT_POWER_CUTOFF ) continue; + if ( validTextureIndex(light.mapIndex) ) { + float factor = shadowFactor( light, light.mapIndex, 0.0 ); + light.power *= factor; + litFactor += light.power; + } + vec3 Li = light.position - position.world; + light.power *= 1.0 / (PI * pow(length(Li), 2.0)); + if ( light.power <= LIGHT_POWER_CUTOFF ) continue; + + Li = normalize(Li); + vec3 Lo = normalize( -position.world ); + vec3 Lh = normalize(Li + Lo); + + vec3 Lradiance = light.color.rgb * light.power; + vec3 albedo = C.rgb; + + float cosLi = max(0.0, dot(N, Li)); + float cosLo = max(0.0, dot(N, Lo)); + float cosLh = max(0.0, dot(N, Lh)); + + vec3 F = fresnelSchlick( F0, max( 0.0, dot(Lh, Lo) ) ); + float D = ndfGGX( cosLh, R ); + float G = gaSchlickGGX(cosLi, cosLo, R); + + vec3 Kd = mix( vec3(1.0) - F, vec3(0.0), M ); + vec3 diffuseBRDF = Kd * albedo; + vec3 specularBRDF = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo); + if ( useLightmap ) { + fragColor.rgb += (specularBRDF) * Lradiance * cosLi; + } else { + fragColor.rgb += (diffuseBRDF + specularBRDF) * Lradiance * cosLi; + } + } + } + imageStore(voxelAlbedo, ivec3(tUvw), vec4(fragColor.rgb, 1)); +} \ No newline at end of file diff --git a/bin/data/shaders/gltf/baked.frag.glsl b/bin/data/shaders/gltf/baked.frag.glsl index d783b1b5..08dee772 100644 --- a/bin/data/shaders/gltf/baked.frag.glsl +++ b/bin/data/shaders/gltf/baked.frag.glsl @@ -57,8 +57,9 @@ layout (location = 1) out vec2 outNormals; #endif vec2 encodeNormals( vec3 n ) { - float p = sqrt(n.z*8+8); - return n.xy/p + 0.5; +// return n.xy / sqrt(n.z*8+8) + 0.5; +#define kPI 3.1415926536f + return (vec2(atan(n.y,n.x)/kPI, n.z)+1.0)*0.5; } float wrap( float i ) { diff --git a/bin/data/shaders/gltf/baking/bake.frag.glsl b/bin/data/shaders/gltf/baking/bake.frag.glsl index 5f29693b..86b9ffd8 100644 --- a/bin/data/shaders/gltf/baking/bake.frag.glsl +++ b/bin/data/shaders/gltf/baking/bake.frag.glsl @@ -172,7 +172,7 @@ float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { vec3 fresnelSchlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } -void pbr( Light light, vec3 albedo, float metallic, float roughness, vec3 lightPositionWorld, vec3 normal, vec3 position, inout vec3 i ) { +void pbr( Light light, vec3 albedo, float metallic, float roughness, vec3 normal, vec3 position, inout vec3 i ) { vec3 F0 = vec3(0.04); F0 = mix(F0, albedo, metallic); @@ -202,7 +202,8 @@ void pbr( Light light, vec3 albedo, float metallic, float roughness, vec3 lightP vec3 specular = numerator / max(denominator, 0.001); // add to outgoing radiance Lo - i += (kD * albedo / PI + specular) * radiance * NdotL; + // ignore specular + i += (kD * albedo / PI ) * radiance * NdotL; } void main() { @@ -210,7 +211,7 @@ void main() { vec2 uv = wrap(inUv.xy); vec4 C = vec4(1, 1, 1, 1); vec3 P = inPosition; - vec3 N = inNormal; + vec3 N = normalize( inNormal ); int materialId = int(inId.y); Material material = materials[materialId]; @@ -248,6 +249,7 @@ void main() { N = inTBN * normalize( textureLod( samplerTextures[(useAtlas)?textureAtlas.index:t.index], ( useAtlas ) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip ).xyz * 2.0 - vec3(1.0)); } #endif + C = vec4(1); bool lit = false; vec3 fragColor = vec3(0); @@ -259,18 +261,7 @@ void main() { light.power *= factor; } if ( light.power <= 0.0001 ) continue; - pbr( light, C.rgb, M, R, light.position.xyz, N, P, fragColor ); - // if ( usePbr ) pbr( light, C.rgb, M, R, lightPositionWorld, fragColor ); else phong( light, C, fragColor ); -/* - Light light = lights[i]; - if ( validTextureIndex(light.mapIndex) ) { - light.power *= shadowFactor( P, light, light.mapIndex, 0.0 ); - } - if ( light.power < 0.001 ) continue; - pbr( light, C.rgb, M, R, light.position.xyz, N, P, fragColor ); - lit = true; -*/ + pbr( light, C.rgb, M, R, N, P, fragColor ); } -// if ( !lit ) fragColor = C.rgb; outAlbedo = vec4(fragColor, 1); } \ No newline at end of file diff --git a/bin/data/shaders/gltf/base.frag.glsl b/bin/data/shaders/gltf/base.frag.glsl index 4dfe0f77..41df8a87 100644 --- a/bin/data/shaders/gltf/base.frag.glsl +++ b/bin/data/shaders/gltf/base.frag.glsl @@ -57,8 +57,9 @@ layout (location = 1) out vec2 outNormals; #endif vec2 encodeNormals( vec3 n ) { - float p = sqrt(n.z*8+8); - return n.xy/p + 0.5; +// return n.xy / sqrt(n.z*8+8) + 0.5; +#define kPI 3.1415926536f + return (vec2(atan(n.y,n.x)/kPI, n.z)+1.0)*0.5; } float wrap( float i ) { return fract(i); @@ -81,7 +82,6 @@ void main() { vec3 P = inPosition; vec3 N = inNormal; #if UF_DEFERRED_SAMPLING - outUvs = wrap(inUv.xy); vec4 outAlbedo = vec4(0,0,0,0); #endif #if !UF_DEFERRED_SAMPLING || UF_CAN_DISCARD @@ -145,6 +145,8 @@ void main() { } #endif outAlbedo = C * inColor; +#else + outUvs = wrap(inUv.xy); #endif outNormals = encodeNormals( N ); outId = ivec2(inId.w+1, inId.y+1); diff --git a/bin/data/shaders/gltf/base.vert.glsl b/bin/data/shaders/gltf/base.vert.glsl index 97b00aec..5bdd45a2 100644 --- a/bin/data/shaders/gltf/base.vert.glsl +++ b/bin/data/shaders/gltf/base.vert.glsl @@ -32,10 +32,6 @@ layout (location = 4) out mat3 outTBN; layout (location = 7) out vec3 outPosition; layout (location = 8) out ivec4 outId; -out gl_PerVertex { - vec4 gl_Position; -}; - vec4 snap(vec4 vertex, vec2 resolution) { vec4 snappedPos = vertex; snappedPos.xyz = vertex.xyz / vertex.w; @@ -50,8 +46,12 @@ void main() { outColor = ubo.color; outId = ivec4(inId, PushConstant.pass, PushConstant.draw); - outPosition = vec3(ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * vec4(inPos.xyz, 1.0)); - outNormal = vec3(ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * vec4(inNormal.xyz, 0.0)); +// outPosition = vec3(ubo.matrices.projection[PushConstant.pass] * ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * vec4(inPos.xyz, 1.0)); +// outPosition = vec3(ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * vec4(inPos.xyz, 1.0)); + outPosition = vec3(ubo.matrices.model * vec4(inPos.xyz, 1.0)); + +// outNormal = vec3(ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * vec4(inNormal.xyz, 0.0)); + outNormal = vec3(ubo.matrices.model * vec4(inNormal.xyz, 0.0)); outNormal = normalize(outNormal); diff --git a/bin/data/shaders/gltf/instanced.vert.glsl b/bin/data/shaders/gltf/instanced.vert.glsl index 7e3ade84..02e4bb9a 100644 --- a/bin/data/shaders/gltf/instanced.vert.glsl +++ b/bin/data/shaders/gltf/instanced.vert.glsl @@ -30,10 +30,6 @@ layout (location = 4) out mat3 outTBN; layout (location = 7) out vec3 outPosition; layout (location = 8) out ivec4 outId; -out gl_PerVertex { - vec4 gl_Position; -}; - vec4 snap(vec4 vertex, vec2 resolution) { vec4 snappedPos = vertex; snappedPos.xyz = vertex.xyz / vertex.w; @@ -49,8 +45,12 @@ void main() { outId = ivec4(inId, PushConstant.pass, PushConstant.draw); mat4 model = models.length() <= 0 ? mat4(1.0) : models[int(inId.x)]; - outPosition = vec3(ubo.view[PushConstant.pass] * model * vec4(inPos.xyz, 1.0)); - outNormal = vec3(ubo.view[PushConstant.pass] * model * vec4(inNormal.xyz, 0.0)); +// outPosition = vec3(ubo.projection[PushConstant.pass] * ubo.view[PushConstant.pass] * model * vec4(inPos.xyz, 1.0)); +// outPosition = vec3(ubo.view[PushConstant.pass] * model * vec4(inPos.xyz, 1.0)); + outPosition = vec3(model * vec4(inPos.xyz, 1.0)); + +// outNormal = vec3(ubo.view[PushConstant.pass] * model * vec4(inNormal.xyz, 0.0)); + outNormal = vec3(model * vec4(inNormal.xyz, 0.0)); outNormal = normalize(outNormal); { diff --git a/bin/data/shaders/gltf/skinned.instanced.vert.glsl b/bin/data/shaders/gltf/skinned.instanced.vert.glsl index 3f05222e..54cd6e09 100644 --- a/bin/data/shaders/gltf/skinned.instanced.vert.glsl +++ b/bin/data/shaders/gltf/skinned.instanced.vert.glsl @@ -36,10 +36,6 @@ layout (location = 4) out mat3 outTBN; layout (location = 7) out vec3 outPosition; layout (location = 8) out ivec4 outId; -out gl_PerVertex { - vec4 gl_Position; -}; - vec4 snap(vec4 vertex, vec2 resolution) { vec4 snappedPos = vertex; snappedPos.xyz = vertex.xyz / vertex.w; @@ -61,8 +57,12 @@ void main() { inWeights.z * joints[int(inJoints.z)] + inWeights.w * joints[int(inJoints.w)]; - outPosition = vec3(ubo.view[PushConstant.pass] * model * skinnedMatrix * vec4(inPos.xyz, 1.0)); - outNormal = vec3(ubo.view[PushConstant.pass] * model * vec4(inNormal.xyz, 0.0)); +// outPosition = vec3(ubo.projection[PushConstant.pass] * ubo.view[PushConstant.pass] * model * skinnedMatrix * vec4(inPos.xyz, 1.0)); +// outPosition = vec3(ubo.view[PushConstant.pass] * model * skinnedMatrix * vec4(inPos.xyz, 1.0)); + outPosition = vec3(model * skinnedMatrix * vec4(inPos.xyz, 1.0)); + +// outNormal = vec3(ubo.view[PushConstant.pass] * model * vec4(inNormal.xyz, 0.0)); + outNormal = vec3(model * vec4(inNormal.xyz, 0.0)); outNormal = normalize(outNormal); diff --git a/bin/data/shaders/gltf/skinned.vert.glsl b/bin/data/shaders/gltf/skinned.vert.glsl index 67050d08..da2832ce 100644 --- a/bin/data/shaders/gltf/skinned.vert.glsl +++ b/bin/data/shaders/gltf/skinned.vert.glsl @@ -38,10 +38,6 @@ layout (location = 4) out mat3 outTBN; layout (location = 7) out vec3 outPosition; layout (location = 8) out ivec4 outId; -out gl_PerVertex { - vec4 gl_Position; -}; - vec4 snap(vec4 vertex, vec2 resolution) { vec4 snappedPos = vertex; snappedPos.xyz = vertex.xyz / vertex.w; @@ -62,8 +58,12 @@ void main() { inWeights.z * joints[int(inJoints.z)] + inWeights.w * joints[int(inJoints.w)]; - outPosition = vec3(ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * skinnedMatrix * vec4(inPos.xyz, 1.0)); - outNormal = vec3(ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * vec4(inNormal.xyz, 0.0)); +// outPosition = vec3(ubo.matrices.projection[PushConstant.pass] * ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * skinnedMatrix * vec4(inPos.xyz, 1.0)); +// outPosition = vec3(ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * skinnedMatrix * vec4(inPos.xyz, 1.0)); + outPosition = vec3(ubo.matrices.model * skinnedMatrix * vec4(inPos.xyz, 1.0)); + +// outNormal = vec3(ubo.matrices.view[PushConstant.pass] * ubo.matrices.model * vec4(inNormal.xyz, 0.0)); + outNormal = vec3(ubo.matrices.model * vec4(inNormal.xyz, 0.0)); outNormal = normalize(outNormal); diff --git a/bin/data/shaders/gltf/voxelize.frag.glsl b/bin/data/shaders/gltf/voxelize.frag.glsl new file mode 100644 index 00000000..7bae0b9a --- /dev/null +++ b/bin/data/shaders/gltf/voxelize.frag.glsl @@ -0,0 +1,136 @@ +#version 450 + +#define UF_DEFERRED_SAMPLING 0 +#define UF_CAN_DISCARD 1 + +layout (constant_id = 0) const uint TEXTURES = 1; + +struct Material { + vec4 colorBase; + vec4 colorEmissive; + + float factorMetallic; + float factorRoughness; + float factorOcclusion; + float factorAlphaCutoff; + + int indexAlbedo; + int indexNormal; + int indexEmissive; + int indexOcclusion; + + int indexMetallicRoughness; + int indexAtlas; + int indexLightmap; + int modeAlpha; +}; +struct Texture { + int index; + int samp; + int remap; + float blend; + + vec4 lerp; +}; +layout (std140, binding = 0) readonly buffer Materials { + Material materials[]; +}; +layout (std140, binding = 1) readonly buffer Textures { + Texture textures[]; +}; + + +layout (location = 0) in vec2 inUv; +layout (location = 1) in vec2 inSt; +layout (location = 2) in vec4 inColor; +layout (location = 3) in vec3 inNormal; +layout (location = 4) in mat3 inTBN; +layout (location = 7) in vec3 inPosition; +layout (location = 8) flat in ivec4 inId; + +layout (binding = 7) uniform sampler2D samplerTextures[TEXTURES]; +layout (binding = 8, rg16ui) uniform volatile coherent uimage3D voxelID; +layout (binding = 9, rg16f) uniform volatile coherent image3D voxelNormal; +layout (binding = 10, rg16f) uniform volatile coherent image3D voxelUv; +layout (binding = 11, rgba8) uniform volatile coherent image3D voxelAlbedo; + +layout (location = 0) out uvec2 outId; +layout (location = 1) out vec2 outNormals; +#if UF_DEFERRED_SAMPLING + layout (location = 2) out vec2 outUvs; +#else + layout (location = 2) out vec4 outAlbedo; +#endif + +vec2 encodeNormals( vec3 n ) { +// return n.xy / sqrt(n.z*8+8) + 0.5; +#define kPI 3.1415926536f + return (vec2(atan(n.y,n.x)/kPI, n.z)+1.0)*0.5; +} +float wrap( float i ) { + return fract(i); +} +vec2 wrap( vec2 uv ) { + return vec2( wrap( uv.x ), wrap( uv.y ) ); +} +float mipLevel( in vec2 uv ) { + vec2 dx_vtc = dFdx(uv); + vec2 dy_vtc = dFdy(uv); + return 0.5 * log2(max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc))); +} +bool validTextureIndex( int textureIndex ) { + return 0 <= textureIndex && textureIndex < textures.length(); +} + +void main() { + vec3 P = inPosition; + if ( !(abs(P.x) < 1.0 && abs(P.y) < 1 && abs(P.z) < 1) ) discard; + + vec4 C = vec4(0, 0, 0, 0); + vec3 N = inNormal; + vec2 uv = wrap(inUv.xy); + float mip = mipLevel(inUv.xy); + + int materialId = int(inId.y); + Material material = materials[materialId]; + + float M = material.factorMetallic; + float R = material.factorRoughness; + float AO = material.factorOcclusion; + + // sample albedo + bool useAtlas = validTextureIndex( material.indexAtlas ); + Texture textureAtlas; + if ( useAtlas ) textureAtlas = textures[material.indexAtlas]; + if ( !validTextureIndex( material.indexAlbedo ) ) discard; { + Texture t = textures[material.indexAlbedo]; + C = textureLod( samplerTextures[(useAtlas) ? textureAtlas.index : t.index], (useAtlas) ? mix( t.lerp.xy, t.lerp.zw, uv ) : uv, mip ); + // alpha mode OPAQUE + if ( material.modeAlpha == 0 ) { + C.a = 1; + // alpha mode BLEND + } else if ( material.modeAlpha == 1 ) { + + // alpha mode MASK + } else if ( material.modeAlpha == 2 ) { + if ( C.a < abs(material.factorAlphaCutoff) ) discard; + C.a = 1; + } + if ( C.a == 0 ) discard; + } + +#if UF_DEFERRED_SAMPLING + vec4 outAlbedo; +#else + vec2 outUvs; +#endif + /*vec4*/ outAlbedo = C * inColor; + /*uvec2*/ outId = uvec2(inId.w+1, inId.y+1); + /*vec2*/ outNormals = encodeNormals( normalize( N ) ); + /*vec2*/ outUvs = wrap(inUv.xy); + + imageStore(voxelID, ivec3(P * imageSize(voxelID)), uvec4(outId, 0, 0)); + imageStore(voxelNormal, ivec3(P * imageSize(voxelNormal)), vec4(outNormals, 0, 0)); + imageStore(voxelUv, ivec3(P * imageSize(voxelUv)), vec4(outUvs, 0, 0)); + imageStore(voxelAlbedo, ivec3(P * imageSize(voxelAlbedo)), outAlbedo); +} \ No newline at end of file diff --git a/bin/data/shaders/gltf/voxelize.geom.glsl b/bin/data/shaders/gltf/voxelize.geom.glsl new file mode 100644 index 00000000..1e51b564 --- /dev/null +++ b/bin/data/shaders/gltf/voxelize.geom.glsl @@ -0,0 +1,63 @@ +#version 450 + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +layout (location = 0) in vec2 inUv[]; +layout (location = 1) in vec2 inSt[]; +layout (location = 2) in vec4 inColor[]; +layout (location = 3) in vec3 inNormal[]; +layout (location = 4) in mat3 inTBN[]; +layout (location = 7) in vec3 inPosition[]; +layout (location = 8) flat in ivec4 inId[]; + +layout (location = 0) out vec2 outUv; +layout (location = 1) out vec2 outSt; +layout (location = 2) out vec4 outColor; +layout (location = 3) out vec3 outNormal; +layout (location = 4) out mat3 outTBN; +layout (location = 7) out vec3 outPosition; +layout (location = 8) flat out ivec4 outId; + +layout (binding = 6) uniform UBO { + mat4 voxel; +} ubo; + +void main(){ + vec3 norm = abs(inNormal[0] + inNormal[1] + inNormal[2]); + uint axis = norm.y > norm.x ? 1 : 0; + axis = norm.z > norm[axis] ? 2 : axis; + + mat4 invOrtho = inverse( ubo.voxel ); + vec3 extentMin = vec3( invOrtho * vec4(-1, -1, -1, 1 ) ); + vec3 extentMid = vec3( invOrtho * vec4( 0, 0, 0, 1 ) ); + vec3 extentMax = vec3( invOrtho * vec4( 1, 1, 1, 1 ) ); + vec3 extentSize = extentMax - extentMin; + + for( uint i = 0; i < 3; ++i ){ + outUv = inUv[i]; + outSt = inSt[i]; + outColor = inColor[i]; + outNormal = inNormal[i]; + outTBN = inTBN[i]; + outPosition = inPosition[i]; + outId = inId[i]; + + outPosition = vec3( ubo.voxel * vec4( outPosition, 1 ) ); + + gl_Position = vec4( outPosition.xyz, 1 ); + if ( axis == 0 ) gl_Position.xyz = gl_Position.zyx; + else if ( axis == 1 ) gl_Position.xyz = gl_Position.xzy; + + // outPosition = (inPosition[i] - extentMin) / extentSize; + // outPosition = fract(outPosition * 0.5 + 0.5); + + outPosition = outPosition * 0.5 + 0.5; + // gl_Position.xy = fract( gl_Position.xy * 0.5 + 0.5 ) * 2.0 - 1.0; + // gl_Position.xy = fract( gl_Position.xy * 0.5 + 0.5 ) * 2.0 - 1.0; + gl_Position.z = 1; + + EmitVertex(); + } + EndPrimitive(); +} \ No newline at end of file diff --git a/engine/inc/uf/ext/gltf/graph.h b/engine/inc/uf/ext/gltf/graph.h index 1ed2eced..c9547e74 100644 --- a/engine/inc/uf/ext/gltf/graph.h +++ b/engine/inc/uf/ext/gltf/graph.h @@ -86,25 +86,9 @@ namespace pod { namespace uf { namespace graph { - /* - pod::Node& UF_API node(); - pod::Node* UF_API find( const pod::Node& node, int32_t index ); - pod::Node* UF_API find( pod::Node* node, int32_t index ); - pod::Node* UF_API find( const pod::Graph& graph, int32_t index ); - - pod::Node* UF_API find( const pod::Node& node, const std::string& name ); - pod::Node* UF_API find( pod::Node* node, const std::string& name ); - pod::Node* UF_API find( const pod::Graph& graph, const std::string& name ); - - pod::Matrix4f UF_API local( const pod::Node& node ); - pod::Matrix4f UF_API matrix( const pod::Node& node ); - */ pod::Node* UF_API find( pod::Graph& graph, int32_t index ); pod::Node* UF_API find( pod::Graph& graph, const std::string& name ); - // pod::Matrix4f UF_API local( const pod::Node& node ); - // pod::Matrix4f UF_API matrix( pod::Graph&, const pod::Node& node ); - pod::Matrix4f UF_API local( pod::Graph&, int32_t ); pod::Matrix4f UF_API matrix( pod::Graph&, int32_t ); @@ -112,6 +96,7 @@ namespace uf { void UF_API process( pod::Graph& graph ); void UF_API process( pod::Graph& graph, int32_t, uf::Object& parent ); void UF_API cleanup( pod::Graph& graph ); + void UF_API initialize( pod::Graph& graph ); void UF_API override( pod::Graph& ); void UF_API animate( pod::Graph&, const std::string&, float = 1, bool = true ); diff --git a/engine/inc/uf/ext/gltf/pod.h b/engine/inc/uf/ext/gltf/pod.h index d72de6c7..2f3c8c24 100644 --- a/engine/inc/uf/ext/gltf/pod.h +++ b/engine/inc/uf/ext/gltf/pod.h @@ -6,6 +6,17 @@ #include namespace pod { + struct UF_API SceneTextures { + uf::renderer::Texture3D noise; + uf::renderer::TextureCube skybox; + struct { + uf::renderer::Texture3D id; + uf::renderer::Texture3D normal; + uf::renderer::Texture3D albedo; + uf::renderer::Texture3D uv; + } voxels; + }; + struct UF_API DrawCall { struct Storage { alignas(4) int32_t materialIndex = -1; diff --git a/engine/inc/uf/ext/json/nlohmann.h b/engine/inc/uf/ext/json/nlohmann.h index 624e1f57..ecd0176e 100644 --- a/engine/inc/uf/ext/json/nlohmann.h +++ b/engine/inc/uf/ext/json/nlohmann.h @@ -97,12 +97,18 @@ template<> inline bool ext::json::Value::is(bool strict) const { re template<> inline bool ext::json::Value::is(bool strict) const { return strict ? is_number_unsigned() : is_number(); } #endif template inline T ext::json::Value::as() const { + return !is() ? T() : get(); +/* if ( !is() ) return T(); return get(); +*/ } template inline T ext::json::Value::as( const T& fallback ) const { + return !is() ? fallback : get(); +/* if ( !is() ) return fallback; return get(); +*/ } template<> inline bool ext::json::Value::as() const { // explicitly a bool diff --git a/engine/inc/uf/ext/vulkan/graphic.h b/engine/inc/uf/ext/vulkan/graphic.h index d88ed040..e338b8b3 100644 --- a/engine/inc/uf/ext/vulkan/graphic.h +++ b/engine/inc/uf/ext/vulkan/graphic.h @@ -34,11 +34,6 @@ namespace ext { VkSpecializationInfo specializationInfo; uf::Serializer metadata; - /* - std::vector specializationConstants; - std::vector pushConstants; - std::vector uniforms; - */ ext::vulkan::userdata_t specializationConstants; std::vector pushConstants; @@ -75,12 +70,16 @@ namespace ext { VkDescriptorSet descriptorSet; GraphicDescriptor descriptor; + uf::Serializer metadata; + void initialize( Graphic& graphic ); void initialize( Graphic& graphic, GraphicDescriptor& descriptor ); void update( Graphic& graphic ); void update( Graphic& graphic, GraphicDescriptor& descriptor ); void record( Graphic& graphic, VkCommandBuffer, size_t = 0, size_t = 0 ); void destroy(); + + std::vector getShaders( std::vector& ); }; struct UF_API Material { bool aliased = false; @@ -94,11 +93,11 @@ namespace ext { void initialize( Device& device ); void destroy(); - void attachShader( const std::string&, VkShaderStageFlagBits ); - void initializeShaders( const std::vector>& ); + void attachShader( const std::string&, VkShaderStageFlagBits, const std::string& pipeline = "" ); + void initializeShaders( const std::vector>&, const std::string& pipeline = "" ); - bool hasShader( const std::string& type ); - Shader& getShader( const std::string& type ); + bool hasShader( const std::string& type, const std::string& pipeline = "" ); + Shader& getShader( const std::string& type, const std::string& pipeline = "" ); bool validate(); }; diff --git a/engine/inc/uf/ext/vulkan/rendermodes/deferred.h b/engine/inc/uf/ext/vulkan/rendermodes/deferred.h index 52f15b2f..9aed403f 100644 --- a/engine/inc/uf/ext/vulkan/rendermodes/deferred.h +++ b/engine/inc/uf/ext/vulkan/rendermodes/deferred.h @@ -13,6 +13,8 @@ namespace ext { virtual const size_t blitters() const; virtual ext::vulkan::Graphic* getBlitter(size_t = 0); virtual std::vector getBlitters(); + + virtual GraphicDescriptor bindGraphicDescriptor( const GraphicDescriptor&, size_t = 0 ); virtual void createCommandBuffers( const std::vector& graphics ); virtual void initialize( Device& device ); diff --git a/engine/inc/uf/ext/vulkan/rendermodes/rendertarget.h b/engine/inc/uf/ext/vulkan/rendermodes/rendertarget.h index 7f6dbce5..b6f46bf0 100644 --- a/engine/inc/uf/ext/vulkan/rendermodes/rendertarget.h +++ b/engine/inc/uf/ext/vulkan/rendermodes/rendertarget.h @@ -6,11 +6,20 @@ namespace ext { namespace vulkan { struct UF_API RenderTargetRenderMode : public ext::vulkan::RenderMode { - ext::vulkan::Graphic blitter; + ext::vulkan::Graphic blitter; + + typedef std::function callback_t; + std::unordered_map commandBufferCallbacks; + // const std::string getTarget() const; void setTarget( const std::string& ); + void bindCallback( int32_t, const callback_t& ); + + constexpr static int32_t CALLBACK_BEGIN = -1; + constexpr static int32_t CALLBACK_END = -2; + // RAII virtual const std::string getType() const; virtual const size_t blitters() const; diff --git a/engine/inc/uf/utils/graphic/descriptor.h b/engine/inc/uf/utils/graphic/descriptor.h index c3b23372..37e89a9b 100644 --- a/engine/inc/uf/utils/graphic/descriptor.h +++ b/engine/inc/uf/utils/graphic/descriptor.h @@ -51,6 +51,8 @@ namespace ext { namespace RENDERER { struct UF_API GraphicDescriptor { std::string renderMode = ""; + std::string pipeline = ""; + uint32_t renderTarget = 0; uint32_t subpass = 0; diff --git a/engine/inc/uf/utils/math/matrix/pod.inl b/engine/inc/uf/utils/math/matrix/pod.inl index 99ce627e..e763ea93 100644 --- a/engine/inc/uf/utils/math/matrix/pod.inl +++ b/engine/inc/uf/utils/math/matrix/pod.inl @@ -48,6 +48,15 @@ pod::Matrix4t /*UF_API*/ uf::matrix::identity() { } template pod::Matrix4t /*UF_API*/ uf::matrix::ortho( T l, T r, T b, T t, T f, T n ) { + pod::Matrix4t m = uf::matrix::identity(); + m[0*4+0] = 2 / (r - l); + m[1*4+1] = 2 / (t - b); + m[2*4+2] = - 2 / (f - n); + m[3*4+0] = - (r + l) / (r - l); + m[3*4+1] = - (t + b) / (t - b); + m[3*4+2] = - (f + n) / (f - n); + return m; +/* std::vector m = { 2 / (r - l), 0, 0, 0, 0, 2 / (t - b), 0, 0, @@ -55,6 +64,7 @@ pod::Matrix4t /*UF_API*/ uf::matrix::ortho( T l, T r, T b, T t, T f, T n ) { -(r + l) / (r - l), -(t + b) / (t - b), -(f + n) / (f - n), 1, }; return uf::matrix::initialize(m); +*/ } template pod::Matrix4t /*UF_API*/ uf::matrix::ortho( T l, T r, T b, T t ) { diff --git a/engine/src/engine/object/behaviors/gltf.cpp b/engine/src/engine/object/behaviors/gltf.cpp index 3fe5090d..345042c5 100644 --- a/engine/src/engine/object/behaviors/gltf.cpp +++ b/engine/src/engine/object/behaviors/gltf.cpp @@ -36,127 +36,34 @@ void uf::GltfBehavior::initialize( uf::Object& self ) { auto& graph = this->getComponent(); graph = std::move( *graphPointer ); assetLoader.remove(filename); - { - bool shouldUpdate = false; - auto& sceneMetadataJson = scene.getComponent(); - if ( !ext::json::isNull(graph.metadata["ambient"]) ) { - sceneMetadataJson["light"]["ambient"] = graph.metadata["ambient"]; - shouldUpdate = true; - } - if ( !ext::json::isNull(graph.metadata["fog"]) ) { - sceneMetadataJson["light"]["fog"] = graph.metadata["fog"]; - shouldUpdate = true; - } - if ( shouldUpdate ) { - scene.callHook("object:UpdateMetadata.%UID%"); - } + + bool shouldUpdate = false; + auto& sceneMetadataJson = scene.getComponent(); + if ( !ext::json::isNull(graph.metadata["ambient"]) ) { + sceneMetadataJson["light"]["ambient"] = graph.metadata["ambient"]; + shouldUpdate = true; } + if ( !ext::json::isNull(graph.metadata["fog"]) ) { + sceneMetadataJson["light"]["fog"] = graph.metadata["fog"]; + shouldUpdate = true; + } + if ( shouldUpdate ) scene.callHook("object:UpdateMetadata.%UID%"); // deferred shader loading - graph.root.entity->process([&]( uf::Entity* entity ) { - if ( !entity->hasComponent() ) return; - auto& graphic = entity->getComponent(); - if ( !(graph.metadata["flags"]["LOAD"].as()) ) { - if ( graph.metadata["flags"]["SEPARATE"].as() ) { - { - std::string filename = "/gltf/base.vert.spv"; - if ( graph.metadata["flags"]["SKINNED"].as() ) { - filename = "/gltf/skinned.vert.spv"; - } - if ( metadata["system"]["renderer"]["shaders"]["vertex"].is() ) - filename = metadata["system"]["renderer"]["shaders"]["vertex"].as(); - filename = this->grabURI( filename, metadata["system"]["root"].as() ); - graphic.material.attachShader(filename, uf::renderer::enums::Shader::VERTEX); - } - { - std::string filename = "/gltf/base.frag.spv"; - if ( metadata["system"]["renderer"]["shaders"]["fragment"].is() ) - filename = metadata["system"]["renderer"]["shaders"]["fragment"].as(); - filename = this->grabURI( filename, metadata["system"]["root"].as() ); - graphic.material.attachShader(filename, uf::renderer::enums::Shader::FRAGMENT); - } - } else { - { - std::string filename = "/gltf/instanced.vert.spv"; - if ( graph.metadata["flags"]["SKINNED"].as() ) { - filename = "/gltf/skinned.instanced.vert.spv"; - } - if ( metadata["system"]["renderer"]["shaders"]["vertex"].is() ) - filename = metadata["system"]["renderer"]["shaders"]["vertex"].as(); - filename = this->grabURI( filename, metadata["system"]["root"].as() ); - graphic.material.attachShader(filename, uf::renderer::enums::Shader::VERTEX); - } - { - std::string filename = "/gltf/base.frag.spv"; - if ( metadata["system"]["renderer"]["shaders"]["fragment"].is() ) - filename = metadata["system"]["renderer"]["shaders"]["fragment"].as(); - filename = this->grabURI( filename, metadata["system"]["root"].as() ); - graphic.material.attachShader(filename, uf::renderer::enums::Shader::FRAGMENT); - } - } - } - #if UF_USE_VULKAN - { - auto& shader = graphic.material.getShader("vertex"); - struct SpecializationConstant { - uint32_t passes = 6; - }; - auto& specializationConstants = shader.specializationConstants.get(); - specializationConstants.passes = uf::renderer::settings::maxViews; - - - ext::json::forEach( shader.metadata["specializationConstants"], [&]( ext::json::Value& sc ){ - std::string name = sc["name"].as(); - if ( name == "PASSES" ) { - sc["value"] = specializationConstants.passes; - } - }); - - } - { - auto& shader = graphic.material.getShader("fragment"); - struct SpecializationConstant { - uint32_t textures = 1; - }; - auto& specializationConstants = shader.specializationConstants.get(); - specializationConstants.textures = graphic.material.textures.size(); - - ext::json::forEach( shader.metadata["specializationConstants"], [&]( ext::json::Value& sc ){ - std::string name = sc["name"].as(); - if ( name == "TEXTURES" ) { - sc["value"] = specializationConstants.textures; - } - }); - - for ( auto& binding : shader.descriptorSetLayoutBindings ) { - if ( binding.descriptorCount > 1 ) - binding.descriptorCount = specializationConstants.textures; - } - } - #endif - graphic.process = true; - }); - auto& transform = this->getComponent>(); graph.root.entity->getComponent>().reference = &transform; - graph.root.entity->initialize(); - graph.root.entity->process([&]( uf::Entity* entity ) { - if ( !entity->hasComponent() ) { - if ( entity->getUid() == 0 ) entity->initialize(); - return; - } - uf::instantiator::bind( "GltfBehavior", *entity ); - uf::instantiator::unbind( "RenderBehavior", *entity ); - if ( entity->getUid() == 0 ) entity->initialize(); - }); + + uf::graph::initialize( graph ); if ( graph.metadata["flags"]["SKINNED"].as() ) { if ( metadata["model"]["animation"].is() ) { uf::graph::animate( graph, metadata["model"]["animation"].as() ); } + /* if ( metadata["model"]["print animations"].as() ) { uf::Serializer json = ext::json::array(); for ( auto pair : graph.animations ) json.emplace_back( pair.first ); } + */ } this->addChild(graph.root.entity->as()); @@ -174,26 +81,28 @@ void uf::GltfBehavior::tick( uf::Object& self ) { auto& graph = this->getComponent(); if ( graph.metadata["flags"]["SKINNED"].as() ) uf::graph::update( graph ); } + /* Update uniforms */ + if ( !this->hasComponent() ) return; + auto& scene = uf::scene::getCurrentScene(); + auto& metadata = this->getComponent(); + auto& graphic = this->getComponent(); + auto& controller = scene.getController(); + auto& controllerTransform = controller.getComponent>(); + auto& camera = controller.getComponent(); + + if ( !graphic.initialized ) return; + + auto* objectWithGraph = this; + while ( objectWithGraph != &scene ) { + if ( objectWithGraph->hasComponent() ) break; + objectWithGraph = &objectWithGraph->getParent().as(); + } + if ( !objectWithGraph->hasComponent() ) return; + auto& graph = objectWithGraph->getComponent(); + #if UF_USE_OPENGL - /* Update uniforms */ if ( this->hasComponent() ) { - auto& scene = uf::scene::getCurrentScene(); - auto& metadata = this->getComponent(); - auto& graphic = this->getComponent(); - auto& controller = scene.getController(); - auto& camera = controller.getComponent(); - - if ( !graphic.initialized ) return; - if ( !graphic.material.hasShader("vertex") ) return; - - auto* objectWithGraph = this; - while ( objectWithGraph != &scene ) { - if ( objectWithGraph->hasComponent() ) break; - objectWithGraph = &objectWithGraph->getParent().as(); - } - if ( !objectWithGraph->hasComponent() ) return; - + if ( graphic.material.hasShader("vertex") ) { auto& shader = graphic.material.getShader("vertex"); - auto& graph = objectWithGraph->getComponent(); auto& mesh = this->getComponent(); if ( !(graph.metadata["flags"]["SEPARATE"].as()) ) { @@ -210,34 +119,36 @@ void uf::GltfBehavior::tick( uf::Object& self ) { } } #elif UF_USE_VULKAN - /* Update uniforms */ if ( this->hasComponent() ) { - auto& scene = uf::scene::getCurrentScene(); - auto& metadata = this->getComponent(); - auto& graphic = this->getComponent(); - auto& controller = scene.getController(); - auto& camera = controller.getComponent(); - - if ( !graphic.initialized ) return; - if ( !graphic.material.hasShader("fragment") ) return; - - auto* objectWithGraph = this; - while ( objectWithGraph != &scene ) { - if ( objectWithGraph->hasComponent() ) break; - objectWithGraph = &objectWithGraph->getParent().as(); + if ( graphic.material.hasShader("fragment") && !(graph.metadata["flags"]["SEPARATE"].as()) ) { + auto& shader = graphic.material.getShader("vertex"); + std::vector instances( graph.nodes.size() ); + for ( size_t i = 0; i < graph.nodes.size(); ++i ) { + auto& node = graph.nodes[i]; + instances[i] = node.entity ? uf::transform::model( node.entity->getComponent>() ) : uf::transform::model( node.transform ); } - if ( !objectWithGraph->hasComponent() ) return; + auto& storageBuffer = *graphic.getStorageBuffer("Models"); + graphic.updateBuffer( (void*) instances.data(), instances.size() * sizeof(pod::Matrix4f), graph.instanceBufferIndex /*storageBuffer*/ ); + } + if ( graphic.material.hasShader("geometry", "svogi") ) { + auto& shader = graphic.material.getShader("geometry", "svogi"); + pod::Vector3f min = uf::vector::decode( graph.metadata["extents"]["min"], pod::Vector3f{} ); + pod::Vector3f max = uf::vector::decode( graph.metadata["extents"]["max"], pod::Vector3f{} ); + + min.x += floor(controllerTransform.position.x); + min.y -= floor(controllerTransform.position.y); + min.z -= floor(controllerTransform.position.z); - auto& graph = objectWithGraph->getComponent(); - if ( !(graph.metadata["flags"]["SEPARATE"].as()) ) { - auto& shader = graphic.material.getShader("vertex"); - std::vector instances( graph.nodes.size() ); - for ( size_t i = 0; i < graph.nodes.size(); ++i ) { - auto& node = graph.nodes[i]; - instances[i] = node.entity ? uf::transform::model( node.entity->getComponent>() ) : uf::transform::model( node.transform ); - } - auto& storageBuffer = *graphic.getStorageBuffer("Models"); - graphic.updateBuffer( (void*) instances.data(), instances.size() * sizeof(pod::Matrix4f), graph.instanceBufferIndex /*storageBuffer*/ ); - } + max.x += floor(controllerTransform.position.x); + max.y -= floor(controllerTransform.position.y); + max.z -= floor(controllerTransform.position.z); + + struct UniformDescriptor { + alignas(16) pod::Matrix4f matrix; + }; + auto& uniform = shader.getUniform("UBO"); + auto& uniforms = uniform.get(); + uniforms.matrix = /*uf::matrix::translate( uf::matrix::identity(), -controllerTransform.position ) **/ uf::matrix::ortho( min.x, max.x, min.y, max.y, min.z, max.z ); + shader.updateUniform( "UBO", uniform ); } #endif } @@ -251,25 +162,28 @@ namespace { } void uf::GltfBehavior::render( uf::Object& self ) { - /* Update uniforms */ if ( this->hasComponent() ) { - auto& scene = uf::scene::getCurrentScene(); - auto& metadata = this->getComponent(); - auto& graphic = this->getComponent(); - auto& controller = scene.getController(); - auto& camera = controller.getComponent(); - auto& transform = this->getComponent>(); + /* Update uniforms */ + if ( !this->hasComponent() ) return; - if ( !graphic.initialized ) return; + auto& scene = uf::scene::getCurrentScene(); + auto& metadata = this->getComponent(); + auto& graphic = this->getComponent(); + auto& controller = scene.getController(); + auto& camera = controller.getComponent(); + auto& transform = this->getComponent>(); - auto* objectWithGraph = this; - while ( objectWithGraph != &scene ) { - if ( objectWithGraph->hasComponent() ) break; - objectWithGraph = &objectWithGraph->getParent().as(); - } - if ( !objectWithGraph->hasComponent() ) return; - auto& graph = objectWithGraph->getComponent(); + if ( !graphic.initialized ) return; + + auto* objectWithGraph = this; + while ( objectWithGraph != &scene ) { + if ( objectWithGraph->hasComponent() ) break; + objectWithGraph = &objectWithGraph->getParent().as(); + } + if ( !objectWithGraph->hasComponent() ) return; + auto& graph = objectWithGraph->getComponent(); #if UF_USE_OPENGL + if ( graphic.material.hasShader("vertex") ) { auto uniformBuffer = graphic.getUniform(); pod::Uniform& uniform = *((pod::Uniform*) graphic.device->getBuffer(uniformBuffer.buffer)); uniform.projection = camera.getProjection(); @@ -279,16 +193,13 @@ void uf::GltfBehavior::render( uf::Object& self ) { uniform.modelView = camera.getView() * uf::transform::model( transform ); } graphic.updateUniform( (void*) &uniform, sizeof(uniform) ); + } #elif UF_USE_VULKAN - if ( !graphic.material.hasShader("fragment") ) return; - if ( !graphic.hasStorage("Materials") ) return; - - // std::cout << "START" << std::endl; + if ( graphic.material.hasShader("vertex") ) { auto& shader = graphic.material.getShader("vertex"); auto& uniform = shader.getUniform("UBO"); if ( !(graph.metadata["flags"]["SEPARATE"].as()) ) { #if UF_UNIFORMS_UPDATE_WITH_JSON - // auto uniforms = shader.getUniformJson("UBO"); ext::json::Value uniforms; for ( std::size_t i = 0; i < uf::renderer::settings::maxViews; ++i ) { uniforms["view"][i] = uf::matrix::encode( camera.getView( i ) ); @@ -342,7 +253,7 @@ void uf::GltfBehavior::render( uf::Object& self ) { shader.updateUniform( "UBO", uniform ); #endif } -#endif } +#endif } #undef this \ No newline at end of file diff --git a/engine/src/engine/scene/scene.cpp b/engine/src/engine/scene/scene.cpp index 9983dc10..1b462433 100644 --- a/engine/src/engine/scene/scene.cpp +++ b/engine/src/engine/scene/scene.cpp @@ -54,6 +54,9 @@ uf::Scene& uf::scene::loadScene( const std::string& name, const std::string& fil */ target = uf::string::lowercase( target ); scene->load(filename != "" ? filename : "./scenes/" + target + "/scene.json"); + if ( uf::renderer::settings::experimental::deferredMode == "svogi" ) { + uf::instantiator::bind( "VoxelizerBehavior", *scene ); + } scene->initialize(); return *scene; } @@ -61,6 +64,9 @@ uf::Scene& uf::scene::loadScene( const std::string& name, const uf::Serializer& uf::Scene* scene = uf::instantiator::objects->has( name ) ? (uf::Scene*) &uf::instantiator::instantiate( name ) : new uf::Scene; uf::scene::scenes.emplace_back( scene ); if ( data != "" ) scene->load(data); + if ( uf::renderer::settings::experimental::deferredMode == "svogi" ) { + uf::instantiator::bind( "VoxelizerBehavior", *scene ); + } scene->initialize(); return *scene; } diff --git a/engine/src/ext/gltf/graph.cpp b/engine/src/ext/gltf/graph.cpp index 488c4c07..d9b82ad7 100644 --- a/engine/src/ext/gltf/graph.cpp +++ b/engine/src/ext/gltf/graph.cpp @@ -12,6 +12,132 @@ #endif namespace { + void initializeShaders( pod::Graph& graph, uf::Object& entity ) { + auto& graphic = entity.getComponent(); + std::string root = uf::io::directory( graph.name ); + size_t texture2Ds = 0; + for ( auto& texture : graphic.material.textures ) { + if ( texture.width > 1 && texture.height > 1 && texture.depth == 1 && texture.layers == 1 ) ++texture2Ds; + } + + // standard pipeline + { + std::string vertexShaderFilename = graph.metadata["shaders"]["vertex"].as("/gltf/base.vert.spv"); + std::string geometryShaderFilename = graph.metadata["shaders"]["geometry"].as(""); + std::string fragmentShaderFilename = graph.metadata["shaders"]["fragment"].as("/gltf/base.frag.spv"); + { + if ( !graph.metadata["flags"]["SEPARATE"].as() ) { + vertexShaderFilename = graph.metadata["flags"]["SKINNED"].as() ? "/gltf/skinned.instanced.vert.spv" : "/gltf/instanced.vert.spv"; + } else if ( graph.metadata["flags"]["SKINNED"].as() ) vertexShaderFilename = "/gltf/skinned.vert.spv"; + vertexShaderFilename = entity.grabURI( vertexShaderFilename, root ); + graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX); + } + if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) { + geometryShaderFilename = entity.grabURI( geometryShaderFilename, root ); + graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY); + } + { + fragmentShaderFilename = entity.grabURI( fragmentShaderFilename, root ); + graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT); + } + #if UF_USE_VULKAN + { + auto& shader = graphic.material.getShader("vertex"); + struct SpecializationConstant { + uint32_t passes = 6; + }; + auto& specializationConstants = shader.specializationConstants.get(); + specializationConstants.passes = uf::renderer::settings::maxViews; + ext::json::forEach( shader.metadata["specializationConstants"], [&]( ext::json::Value& sc ){ + if ( sc["name"].as() == "PASSES" ) sc["value"] = specializationConstants.passes; + }); + } + { + auto& shader = graphic.material.getShader("fragment"); + struct SpecializationConstant { + uint32_t textures = 1; + }; + auto& specializationConstants = shader.specializationConstants.get(); + specializationConstants.textures = texture2Ds; + ext::json::forEach( shader.metadata["specializationConstants"], [&]( ext::json::Value& sc ){ + if ( sc["name"].as() == "TEXTURES" ) sc["value"] = specializationConstants.textures; + }); + for ( auto& binding : shader.descriptorSetLayoutBindings ) { + if ( binding.descriptorCount > 1 ) binding.descriptorCount = specializationConstants.textures; + } + } + #endif + } + // svogi pipeline + if ( uf::renderer::settings::experimental::deferredMode == "svogi" ) { + std::string vertexShaderFilename = graph.metadata["shaders"]["vertex"].as("/gltf/base.vert.spv"); + std::string geometryShaderFilename = graph.metadata["shaders"]["geometry"].as("/gltf/voxelize.geom.spv"); + std::string fragmentShaderFilename = graph.metadata["shaders"]["fragment"].as("/gltf/voxelize.frag.spv"); + /* + { + if ( !graph.metadata["flags"]["SEPARATE"].as() ) { + vertexShaderFilename = graph.metadata["flags"]["SKINNED"].as() ? "/gltf/skinned.instanced.vert.spv" : "/gltf/instanced.vert.spv"; + } else if ( graph.metadata["flags"]["SKINNED"].as() ) vertexShaderFilename = "/gltf/skinned.vert.spv"; + vertexShaderFilename = entity.grabURI( vertexShaderFilename, root ); + graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX, "svogi"); + } + */ + if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) { + geometryShaderFilename = entity.grabURI( geometryShaderFilename, root ); + graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY, "svogi"); + } + { + fragmentShaderFilename = entity.grabURI( fragmentShaderFilename, root ); + graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "svogi"); + } + #if UF_USE_VULKAN + /* + { + auto& shader = graphic.material.getShader("vertex", "svogi"); + struct SpecializationConstant { + uint32_t passes = 6; + }; + auto& specializationConstants = shader.specializationConstants.get(); + specializationConstants.passes = uf::renderer::settings::maxViews; + ext::json::forEach( shader.metadata["specializationConstants"], [&]( ext::json::Value& sc ){ + if ( sc["name"].as() == "PASSES" ) sc["value"] = specializationConstants.passes; + }); + } + */ + /* + if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) { + auto& shader = graphic.material.getShader("geometry", "svogi"); + pod::Vector3f min = uf::vector::decode( graph.metadata["extents"]["min"], pod::Vector3f{} ); + pod::Vector3f max = uf::vector::decode( graph.metadata["extents"]["max"], pod::Vector3f{} ); + + struct UniformDescriptor { + alignas(16) pod::Matrix4f matrix; + }; + + auto& uniform = shader.getUniform("UBO"); + auto& uniforms = uniform.get(); + uniforms.matrix = uf::matrix::ortho( min.x, max.x, min.y, max.y, max.z, min.z ); + shader.updateUniform( "UBO", uniform ); + } + */ + { + auto& shader = graphic.material.getShader("fragment", "svogi"); + struct SpecializationConstant { + uint32_t textures = 1; + }; + auto& specializationConstants = shader.specializationConstants.get(); + specializationConstants.textures = texture2Ds; + ext::json::forEach( shader.metadata["specializationConstants"], [&]( ext::json::Value& sc ){ + if ( sc["name"].as() == "TEXTURES" ) sc["value"] = specializationConstants.textures; + }); + for ( auto& binding : shader.descriptorSetLayoutBindings ) { + if ( binding.descriptorCount > 1 ) binding.descriptorCount = specializationConstants.textures; + } + } + #endif + } + graphic.process = true; + } void initializeGraphics( pod::Graph& graph, uf::Object& entity ) { auto& graphic = entity.getComponent(); graphic.device = &uf::renderer::device; @@ -37,44 +163,18 @@ namespace { for ( auto& sampler : graph.samplers ) { graphic.material.samplers.emplace_back( sampler ); } + // bind scene's voxel texture + if ( uf::renderer::settings::experimental::deferredMode == "svogi" ) { + auto& scene = uf::scene::getCurrentScene(); + auto& sceneTextures = scene.getComponent(); + graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.id); + graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.normal); + graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.uv); + graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.albedo); + } } if ( graph.metadata["flags"]["LOAD"].as() ) { - if ( graph.metadata["flags"]["SEPARATE"].as() ) { - if ( graph.metadata["flags"]["SKINNED"].as() ) { - graphic.material.attachShader(uf::io::root + "/shaders/gltf/skinned.vert.spv", uf::renderer::enums::Shader::VERTEX); - } else { - graphic.material.attachShader(uf::io::root + "/shaders/gltf/base.vert.spv", uf::renderer::enums::Shader::VERTEX); - } - } else { - if ( graph.metadata["flags"]["SKINNED"].as() ) { - graphic.material.attachShader(uf::io::root + "/shaders/gltf/skinned.instanced.vert.spv", uf::renderer::enums::Shader::VERTEX); - } else { - graphic.material.attachShader(uf::io::root + "/shaders/gltf/instanced.vert.spv", uf::renderer::enums::Shader::VERTEX); - } - } - graphic.material.attachShader(uf::io::root + "/shaders/gltf/base.frag.spv", uf::renderer::enums::Shader::FRAGMENT); - #if UF_USE_VULKAN - { - auto& shader = graphic.material.getShader("vertex"); - struct SpecializationConstant { - uint32_t passes = 6; - }; - auto& specializationConstants = shader.specializationConstants.get(); - specializationConstants.passes = uf::renderer::settings::maxViews; - } - { - auto& shader = graphic.material.getShader("fragment"); - struct SpecializationConstant { - uint32_t textures = 1; - }; - auto& specializationConstants = shader.specializationConstants.get(); - specializationConstants.textures = graphic.material.textures.size(); - for ( auto& binding : shader.descriptorSetLayoutBindings ) { - if ( binding.descriptorCount > 1 ) - binding.descriptorCount = specializationConstants.textures; - } - } - #endif + initializeShaders( graph, entity ); } else { graphic.process = false; } @@ -148,16 +248,10 @@ void uf::graph::process( pod::Graph& graph ) { // invalidate our ST's if we're in OpenGL } else { #if UF_USE_OPENGL - for ( auto& mesh : graph.meshes ) { - for ( auto& v : mesh.vertices ) { - v.st = pod::Vector2f{0,0}; - } - } + for ( auto& mesh : graph.meshes ) for ( auto& v : mesh.vertices ) v.st = pod::Vector2f{0,0}; #endif } if ( graph.metadata["flags"]["ATLAS"].as() && graph.atlas.generated() ) { - // for ( auto& texture : graph.textures ) if ( 0 <= texture.storage.index ) ++texture.storage.index; - auto& image = *graph.images.emplace(graph.images.begin(), graph.atlas.getAtlas()); auto& texture = *graph.textures.emplace(graph.textures.begin()); texture.name = "atlas"; @@ -213,16 +307,6 @@ void uf::graph::process( pod::Graph& graph ) { texture.texture.loadFromImage( image ); } } -/* - { - size_t textureSlot = 0; - for ( auto& texture : graph.textures ) { - texture.storage.index = -1; - if ( !texture.bind ) continue; - texture.storage.index = textureSlot++; - } - } -*/ if ( !graph.root.entity ) graph.root.entity = new uf::Object; for ( auto index : graph.root.children ) process( graph, index, *graph.root.entity ); @@ -272,8 +356,6 @@ void uf::graph::process( pod::Graph& graph ) { std::vector textures( graph.textures.size() ); for ( size_t i = 0; i < graph.textures.size(); ++i ) { textures[i] = graph.textures[i].storage; - // textures[i].index = i; - // textures[i].remap = -1; } graph.root.textureBufferIndex = graphic.initializeBuffer( (void*) textures.data(), @@ -282,12 +364,43 @@ void uf::graph::process( pod::Graph& graph ) { ); } + // calculate extents + pod::Vector3f extentMin = { std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; + pod::Vector3f extentMax = { -std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max() }; + graph.root.entity->process([&]( uf::Entity* entity ) { if ( !entity->hasComponent() ) return; + auto& transform = entity->getComponent>(); auto& mesh = entity->getComponent(); auto& metadata = entity->getComponent(); std::string nodeName = metadata["system"]["graph"]["name"].as(); + + pod::Vector3f min = { std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; + pod::Vector3f max = { -std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max() }; + + auto model = uf::transform::model( transform ); + for ( auto& vertex : mesh.vertices ) { + // auto position = uf::matrix::multiply( model, vertex.position, 1.0f ); + min.x = std::min( min.x, vertex.position.x ); + min.y = std::min( min.y, vertex.position.y ); + min.z = std::min( min.z, vertex.position.z ); + + max.x = std::max( max.x, vertex.position.x ); + max.y = std::max( max.y, vertex.position.y ); + max.z = std::max( max.z, vertex.position.z ); + } + + min = uf::matrix::multiply( model, min, 1.0f ); + max = uf::matrix::multiply( model, max, 1.0f ); + + extentMin.x = std::min( min.x, extentMin.x ); + extentMin.y = std::min( min.y, extentMin.y ); + extentMin.z = std::min( min.z, extentMin.z ); + + extentMax.x = std::max( max.x, extentMax.x ); + extentMax.y = std::max( max.y, extentMax.y ); + extentMax.z = std::max( max.z, extentMax.z ); #if 1 if ( graph.metadata["flags"]["NORMALS"].as() ) { // bool invert = false; @@ -391,6 +504,9 @@ void uf::graph::process( pod::Graph& graph ) { } } }); + + if ( ext::json::isNull( graph.metadata["extents"]["min"] ) ) graph.metadata["extents"]["min"] = uf::vector::encode( extentMin * graph.metadata["extents"]["scale"].as(1.0f) ); + if ( ext::json::isNull( graph.metadata["extents"]["max"] ) ) graph.metadata["extents"]["max"] = uf::vector::encode( extentMax * graph.metadata["extents"]["scale"].as(1.0f) ); } void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) { auto& node = graph.nodes[index]; @@ -603,6 +719,20 @@ void uf::graph::override( pod::Graph& graph ) { } } +void uf::graph::initialize( pod::Graph& graph ) { + graph.root.entity->initialize(); + graph.root.entity->process([&]( uf::Entity* entity ) { + if ( !entity->hasComponent() ) { + if ( entity->getUid() == 0 ) entity->initialize(); + return; + } + if ( !graph.metadata["flags"]["LOAD"].as() ) initializeShaders( graph, entity->as() ); + uf::instantiator::bind( "GltfBehavior", *entity ); + uf::instantiator::unbind( "RenderBehavior", *entity ); + if ( entity->getUid() == 0 ) entity->initialize(); + }); +} + void uf::graph::animate( pod::Graph& graph, const std::string& name, float speed, bool immediate ) { if ( !(graph.metadata["flags"]["SKINNED"].as()) ) return; if ( graph.animations.count( name ) > 0 ) { diff --git a/engine/src/ext/vulkan/graphic.cpp b/engine/src/ext/vulkan/graphic.cpp index 429de986..092e439d 100644 --- a/engine/src/ext/vulkan/graphic.cpp +++ b/engine/src/ext/vulkan/graphic.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -798,9 +799,11 @@ void ext::vulkan::Pipeline::initialize( Graphic& graphic, GraphicDescriptor& des this->device = graphic.device; //this->descriptor = descriptor; Device& device = *graphic.device; + metadata["name"] = descriptor.pipeline; // VK_VALIDATION_MESSAGE(&graphic << ": Shaders: " << graphic.material.shaders.size() << " Textures: " << graphic.material.textures.size()); - assert( graphic.material.shaders.size() > 0 ); + auto shaders = getShaders( graphic.material.shaders ); + assert( shaders.size() > 0 ); RenderMode& renderMode = ext::vulkan::getRenderMode( descriptor.renderMode, true); auto& renderTarget = renderMode.getRenderTarget( descriptor.renderTarget ); @@ -810,7 +813,9 @@ void ext::vulkan::Pipeline::initialize( Graphic& graphic, GraphicDescriptor& des std::vector poolSizes; std::unordered_map descriptorTypes; - for ( auto& shader : graphic.material.shaders ) { + // for ( auto& shader : graphic.material.shaders ) { + for ( auto* shaderPointer : shaders ) { + auto& shader = *shaderPointer; descriptorSetLayoutBindings.insert( descriptorSetLayoutBindings.begin(), shader.descriptorSetLayoutBindings.begin(), shader.descriptorSetLayoutBindings.end() ); std::size_t offset = 0; @@ -868,7 +873,8 @@ void ext::vulkan::Pipeline::initialize( Graphic& graphic, GraphicDescriptor& des VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); } // Compute - for ( auto& shader : graphic.material.shaders ) { + for ( auto* shaderPointer : shaders ) { + auto& shader = *shaderPointer; if ( shader.descriptor.stage != VK_SHADER_STAGE_COMPUTE_BIT ) continue; // Create compute shader pipelines @@ -1002,7 +1008,9 @@ void ext::vulkan::Pipeline::initialize( Graphic& graphic, GraphicDescriptor& des vertexInputState.pVertexAttributeDescriptions = vertexAttributeDescriptions.data(); std::vector shaderDescriptors; - for ( auto& shader : graphic.material.shaders ) { + for ( auto* shaderPointer : shaders ) { + auto& shader = *shaderPointer; + void* s = (void*) shader.specializationConstants; size_t len = shader.specializationConstants.data().len; for ( size_t i = 0; i < len / 4; ++i ) { @@ -1081,7 +1089,10 @@ PIPELINE_INITIALIZATION_INVALID: } void ext::vulkan::Pipeline::record( Graphic& graphic, VkCommandBuffer commandBuffer, size_t pass, size_t draw ) { auto bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - for ( auto& shader : graphic.material.shaders ) { + auto shaders = getShaders( graphic.material.shaders ); + for ( auto* shaderPointer : shaders ) { + auto& shader = *shaderPointer; + if ( shader.descriptor.stage == VK_SHADER_STAGE_COMPUTE_BIT ) { bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE; } @@ -1122,7 +1133,9 @@ void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descrip auto& renderTarget = renderMode.getRenderTarget(descriptor.renderTarget ); std::vector descriptorSetLayoutBindings; - for ( auto& shader : graphic.material.shaders ) { + auto shaders = getShaders( graphic.material.shaders ); + for ( auto* shaderPointer : shaders ) { + auto& shader = *shaderPointer; descriptorSetLayoutBindings.insert( descriptorSetLayoutBindings.begin(), shader.descriptorSetLayoutBindings.begin(), shader.descriptorSetLayoutBindings.end() ); } @@ -1155,7 +1168,9 @@ void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descrip } ext::json::Value bindingMapping; - for ( auto& shader : graphic.material.shaders ) { + for ( auto* shaderPointer : shaders ) { + auto& shader = *shaderPointer; + #define PARSE_BUFFER( buffers ) for ( auto& buffer : buffers ) {\ if ( buffer.usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ) {\ infos.uniform.emplace_back(buffer.descriptor);\ @@ -1180,7 +1195,9 @@ void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descrip consumes += layout.descriptorCount; std::string imageType = ""; std::string binding = std::to_string(layout.binding); - for ( auto& shader : graphic.material.shaders ) { + for ( auto* shaderPointer : shaders ) { + auto& shader = *shaderPointer; + auto& info = shader.metadata["definitions"]["textures"][binding]; if ( ext::json::isNull(info) ) continue; imageType = info["type"].as(); @@ -1211,11 +1228,12 @@ void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descrip #define BREAK_ASSERT(condition, ...) if ( condition ) { VK_VALIDATION_MESSAGE(#condition << "\t" << __VA_ARGS__); break; } std::vector writeDescriptorSets; - for ( auto& shader : graphic.material.shaders ) { - // std::cout << shader.filename << ": " << std::endl; - // std::cout << "\tAVAILABLE UNIFORM BUFFERS: " << infos.uniform.size() << std::endl; - // std::cout << "\tAVAILABLE STORAGE BUFFERS: " << infos.storage.size() << std::endl; - // std::cout << "\tCONSUMING : " << shader.descriptorSetLayoutBindings.size() << std::endl; + for ( auto* shaderPointer : shaders ) { + auto& shader = *shaderPointer; + // UF_DEBUG_MSG(shader.filename << ": "); + // UF_DEBUG_MSG("\tAVAILABLE UNIFORM BUFFERS: " << infos.uniform.size()); + // UF_DEBUG_MSG("\tAVAILABLE STORAGE BUFFERS: " << infos.storage.size()); + // UF_DEBUG_MSG("\tCONSUMING : " << shader.descriptorSetLayoutBindings.size()); for ( auto& layout : shader.descriptorSetLayoutBindings ) { // VK_VALIDATION_MESSAGE(shader.filename << "\tType: " << layout.descriptorType << "\tConsuming: " << layout.descriptorCount); @@ -1224,7 +1242,14 @@ void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descrip case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: { - // std::cout << "\tINSERTING IMAGE" << std::endl; + // UF_DEBUG_MSG("\t["<< layout.binding << "] INSERTING " << layout.descriptorCount << " IMAGE"); + // if ( layout.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ) UF_DEBUG_MSG("\t\tCOMBINED_IMAGE_SAMPLER"); + // if ( layout.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE ) UF_DEBUG_MSG("\t\tSAMPLED_IMAGE"); + // if ( layout.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ) UF_DEBUG_MSG("\t\tSTORAGE_IMAGE"); + if ( layout.descriptorCount == 1 ) { + VkDescriptorImageInfo i = (*imageInfo); + // UF_DEBUG_MSG(i.imageView << "\t" << i.imageLayout); + } BREAK_ASSERT( imageInfo == infos.image.end(), "Filename: " << shader.filename << "\tCount: " << layout.descriptorCount ) writeDescriptorSets.push_back(ext::vulkan::initializers::writeDescriptorSet( descriptorSet, @@ -1236,7 +1261,7 @@ void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descrip imageInfo += layout.descriptorCount; } break; case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { - // std::cout << "\tINSERTING INPUT_ATTACHMENT" << std::endl; + // UF_DEBUG_MSG("\t["<< layout.binding << "] INSERTING " << layout.descriptorCount << " INPUT_ATTACHMENT"); BREAK_ASSERT( inputInfo == infos.input.end(), "Filename: " << shader.filename << "\tCount: " << layout.descriptorCount ) writeDescriptorSets.push_back(ext::vulkan::initializers::writeDescriptorSet( descriptorSet, @@ -1248,7 +1273,7 @@ void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descrip inputInfo += layout.descriptorCount; } break; case VK_DESCRIPTOR_TYPE_SAMPLER: { - // std::cout << "\tINSERTING SAMPLER" << std::endl; + // UF_DEBUG_MSG("\t["<< layout.binding << "] INSERTING " << layout.descriptorCount << " SAMPLER"); BREAK_ASSERT( samplerInfo == infos.sampler.end(), "Filename: " << shader.filename << "\tCount: " << layout.descriptorCount ) writeDescriptorSets.push_back(ext::vulkan::initializers::writeDescriptorSet( descriptorSet, @@ -1260,7 +1285,7 @@ void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descrip samplerInfo += layout.descriptorCount; } break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { - // std::cout << "\tINSERTING UNIFORM_BUFFER" << std::endl; + // UF_DEBUG_MSG("\t["<< layout.binding << "] INSERTING " << layout.descriptorCount << " UNIFORM_BUFFER"); BREAK_ASSERT( uniformBufferInfo == infos.uniform.end(), "Filename: " << shader.filename << "\tCount: " << layout.descriptorCount ) writeDescriptorSets.push_back(ext::vulkan::initializers::writeDescriptorSet( descriptorSet, @@ -1272,7 +1297,7 @@ void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descrip uniformBufferInfo += layout.descriptorCount; } break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { - // std::cout << "\tINSERTING STORAGE_BUFFER" << std::endl; + // UF_DEBUG_MSG("\t["<< layout.binding << "] INSERTING " << layout.descriptorCount << " STORAGE_BUFFER"); BREAK_ASSERT( storageBufferInfo == infos.storage.end(), "Filename: " << shader.filename << "\tCount: " << layout.descriptorCount ) writeDescriptorSets.push_back(ext::vulkan::initializers::writeDescriptorSet( descriptorSet, @@ -1306,7 +1331,9 @@ void ext::vulkan::Pipeline::update( Graphic& graphic, GraphicDescriptor& descrip auto pointer = const_cast(&descriptor.pImageInfo[i]); std::string binding = std::to_string(descriptor.dstBinding); std::string imageType = ""; - for ( auto& shader : graphic.material.shaders ) { + for ( auto* shaderPointer : shaders ) { + auto& shader = *shaderPointer; + auto& info = shader.metadata["definitions"]["textures"][binding]; if ( ext::json::isNull(info) ) continue; imageType = info["type"].as(); @@ -1379,6 +1406,45 @@ void ext::vulkan::Pipeline::destroy() { descriptorSetLayout = VK_NULL_HANDLE; } } +std::vector ext::vulkan::Pipeline::getShaders( std::vector& shaders ) { + std::string pipelineName = metadata["name"].as(); + std::unordered_map map; + std::vector res; + + for ( auto& shader : shaders ) { + std::string target = shader.metadata["pipeline"].as(); + std::string type = shader.metadata["type"].as(); + if ( target != "" && target != pipelineName ) continue; + map[type] = &shader; + } + for ( auto pair : map ) { + res.insert( res.begin(), pair.second); + } + +/* + std::string pipelineName = metadata["name"].as(); + std::vector res; + std::unordered_map map; + + // first push base pipeline + for ( auto& shader : shaders ) { + if ( shader.metadata["pipeline"].as() != "" ) continue; + map[shader.metadata["type"].as()] = res.size(); + res.emplace_back(&shader); + } + // then add requested ones + if ( pipelineName != "" ) for ( auto& shader : shaders ) { + if ( shader.metadata["pipeline"].as() != pipelineName ) continue; + if ( map.count(shader.metadata["type"].as()) == 0 ) { + res.emplace_back(&shader); + } else { + size_t index = map.at(shader.metadata["type"].as()); + res[index] = &shader; + } + } +*/ + return res; +} void ext::vulkan::Material::initialize( Device& device ) { this->device = &device; @@ -1394,7 +1460,7 @@ void ext::vulkan::Material::destroy() { textures.clear(); samplers.clear(); } -void ext::vulkan::Material::attachShader( const std::string& filename, VkShaderStageFlagBits stage ) { +void ext::vulkan::Material::attachShader( const std::string& filename, VkShaderStageFlagBits stage, const std::string& pipeline ) { auto& shader = shaders.emplace_back(); shader.initialize( *device, filename, stage ); @@ -1422,24 +1488,27 @@ void ext::vulkan::Material::attachShader( const std::string& filename, VkShaderS case VK_SHADER_STAGE_INTERSECTION_BIT_KHR: type = "intersection"; break; case VK_SHADER_STAGE_CALLABLE_BIT_KHR: type = "callable"; break; } - metadata["shaders"][type]["index"] = shaders.size() - 1; - metadata["shaders"][type]["filename"] = filename; + shader.metadata["pipeline"] = pipeline; + shader.metadata["type"] = type; + + metadata["shaders"][pipeline][type]["index"] = shaders.size() - 1; + metadata["shaders"][pipeline][type]["filename"] = filename; } -void ext::vulkan::Material::initializeShaders( const std::vector>& layout ) { +void ext::vulkan::Material::initializeShaders( const std::vector>& layout, const std::string& pipeline ) { shaders.clear(); shaders.reserve( layout.size() ); for ( auto& request : layout ) { - attachShader( request.first, request.second ); + attachShader( request.first, request.second, pipeline ); } } -bool ext::vulkan::Material::hasShader( const std::string& type ) { - return !ext::json::isNull( metadata["shaders"][type] ); +bool ext::vulkan::Material::hasShader( const std::string& type, const std::string& pipeline ) { + return !ext::json::isNull( metadata["shaders"][pipeline][type] ); } -ext::vulkan::Shader& ext::vulkan::Material::getShader( const std::string& type ) { - if ( !hasShader(type) ) { +ext::vulkan::Shader& ext::vulkan::Material::getShader( const std::string& type, const std::string& pipeline ) { + if ( !hasShader(type, pipeline) ) { static ext::vulkan::Shader null; return null; } - size_t index = metadata["shaders"][type]["index"].as(); + size_t index = metadata["shaders"][pipeline][type]["index"].as(); return shaders.at(index); } bool ext::vulkan::Material::validate() { @@ -1455,6 +1524,10 @@ ext::vulkan::Graphic::~Graphic() { void ext::vulkan::Graphic::initialize( const std::string& renderModeName ) { RenderMode& renderMode = ext::vulkan::getRenderMode(renderModeName, true); +// if ( !uf::Camera::USE_REVERSE_INFINITE_PROJECTION && descriptor.depth.operation == ext::vulkan::enums::Compare::GREATER_OR_EQUAL ) { +// descriptor.depth.operation = ext::vulkan::enums::Compare::LESS_OR_EQUAL; +// } + this->descriptor.renderMode = renderModeName; auto* device = renderMode.device; if ( !device ) device = &ext::vulkan::device; diff --git a/engine/src/ext/vulkan/rendermode.cpp b/engine/src/ext/vulkan/rendermode.cpp index 55ff5336..883c5ae0 100644 --- a/engine/src/ext/vulkan/rendermode.cpp +++ b/engine/src/ext/vulkan/rendermode.cpp @@ -253,6 +253,12 @@ void ext::vulkan::RenderMode::bindPipelines( const std::vector() == descriptor.pipeline ) ++shaders; + } + if ( shaders == 0 ) continue; graphic.initializePipeline( descriptor ); } } diff --git a/engine/src/ext/vulkan/rendermodes/deferred.cpp b/engine/src/ext/vulkan/rendermodes/deferred.cpp index e9db4a1e..b98032b7 100644 --- a/engine/src/ext/vulkan/rendermodes/deferred.cpp +++ b/engine/src/ext/vulkan/rendermodes/deferred.cpp @@ -44,7 +44,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) { size_t eyes = metadata["eyes"].as(); for ( size_t eye = 0; eye < eyes; ++eye ) { struct { - size_t id, normals, uvs, albedo, depth, output; + size_t id, normals, uvs, albedo, depth, output, debug; } attachments; size_t msaa = ext::vulkan::settings::msaa; @@ -122,6 +122,15 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) { }); } metadata["outputs"].emplace_back(attachments.output); + + attachments.debug = renderTarget.attach(RenderTarget::Attachment::Descriptor{ + /*.format = */VK_FORMAT_R16G16B16A16_SFLOAT, + /*.layout = */VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + /*.usage = */VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, + /*.blend = */false, + /*.samples = */1, + }); + if ( ext::vulkan::settings::experimental::deferredMode == "deferredSampling" ) { // First pass: fill the G-Buffer { @@ -138,7 +147,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) { { renderTarget.addPass( /*.*/ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_INPUT_ATTACHMENT_READ_BIT, - /*.colors =*/ { attachments.output }, + /*.colors =*/ { attachments.output, attachments.debug }, /*.inputs =*/ { attachments.id, attachments.normals, attachments.uvs, attachments.depth }, /*.resolve =*/ {}, /*.depth = */ attachments.depth, @@ -161,7 +170,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) { { renderTarget.addPass( /*.*/ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_INPUT_ATTACHMENT_READ_BIT, - /*.colors =*/ { attachments.output }, + /*.colors =*/ { attachments.output, attachments.debug }, /*.inputs =*/ { attachments.id, attachments.normals, attachments.albedo, attachments.depth }, /*.resolve =*/ {}, /*.depth = */ attachments.depth, @@ -189,11 +198,17 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) { blitter.initialize( this->getName() ); blitter.initializeMesh( mesh ); - - blitter.material.initializeShaders({ - {uf::io::root+"/shaders/display/subpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT}, - {uf::io::root+"/shaders/display/subpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT} - }); + if ( ext::vulkan::settings::experimental::deferredMode == "svogi" ) { + blitter.material.initializeShaders({ + {uf::io::root+"/shaders/display/subpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT}, + {uf::io::root+"/shaders/display/subpass.svogi.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT} + }); + } else { + blitter.material.initializeShaders({ + {uf::io::root+"/shaders/display/subpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT}, + {uf::io::root+"/shaders/display/subpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT} + }); + } { auto& scene = uf::scene::getCurrentScene(); @@ -266,6 +281,11 @@ void ext::vulkan::DeferredRenderMode::destroy() { ext::vulkan::RenderMode::destroy(); blitter.destroy(); } + +ext::vulkan::GraphicDescriptor ext::vulkan::DeferredRenderMode::bindGraphicDescriptor( const ext::vulkan::GraphicDescriptor& reference, size_t pass ) { + ext::vulkan::GraphicDescriptor descriptor = ext::vulkan::RenderMode::bindGraphicDescriptor(reference, pass); + return descriptor; +} void ext::vulkan::DeferredRenderMode::createCommandBuffers( const std::vector& graphics ) { float width = this->width > 0 ? this->width : ext::vulkan::settings::width; float height = this->height > 0 ? this->height : ext::vulkan::settings::height; diff --git a/engine/src/ext/vulkan/rendermodes/rendertarget.cpp b/engine/src/ext/vulkan/rendermodes/rendertarget.cpp index 56530185..bb07e5f8 100644 --- a/engine/src/ext/vulkan/rendermodes/rendertarget.cpp +++ b/engine/src/ext/vulkan/rendermodes/rendertarget.cpp @@ -6,6 +6,7 @@ #include #include #include +#include const std::string ext::vulkan::RenderTargetRenderMode::getTarget() const { auto& metadata = *const_cast(&this->metadata); @@ -14,6 +15,9 @@ const std::string ext::vulkan::RenderTargetRenderMode::getTarget() const { void ext::vulkan::RenderTargetRenderMode::setTarget( const std::string& target ) { this->metadata["target"] = target; } +void ext::vulkan::RenderTargetRenderMode::bindCallback( int32_t subpass, const ext::vulkan::RenderTargetRenderMode::callback_t& callback ) { + commandBufferCallbacks[subpass] = callback; +} const std::string ext::vulkan::RenderTargetRenderMode::getType() const { return "RenderTarget"; @@ -33,7 +37,11 @@ ext::vulkan::GraphicDescriptor ext::vulkan::RenderTargetRenderMode::bindGraphicD descriptor.parse(metadata["descriptor"]); std::string type = metadata["type"].as(); std::string target = metadata["target"].as(); - if ( type == "depth" ) { + if ( pass == 0 && type == "svogi" ) { + descriptor.cullMode = VK_CULL_MODE_NONE; + descriptor.depth.test = false; + descriptor.pipeline = "svogi"; + } else if ( type == "depth" ) { descriptor.cullMode = VK_CULL_MODE_NONE; } // invalidate @@ -55,7 +63,7 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) { if ( subpasses == 0 ) subpasses = 1; renderTarget.device = &device; for ( size_t currentPass = 0; currentPass < subpasses; ++currentPass ) { - if ( type == "depth" ) { + if ( type == "depth" /*|| type == "svogi"*/ ) { struct { size_t depth; } attachments; @@ -273,59 +281,155 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) { mesh.indices = { 0, 1, 2, 2, 3, 0 }; + blitter.device = &device; blitter.material.device = &device; blitter.initializeMesh( mesh ); - blitter.material.initializeShaders({ - {uf::io::root+"/shaders/display/renderTarget.vert.spv", VK_SHADER_STAGE_VERTEX_BIT}, - {uf::io::root+"/shaders/display/renderTarget.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT} - }); - - for ( auto& attachment : renderTarget.attachments ) { - if ( !(attachment.descriptor.usage & VK_IMAGE_USAGE_SAMPLED_BIT) ) continue; + if ( ext::json::isArray( metadata["shaders"] ) ) { + ext::json::forEach( metadata["shaders"], [&]( ext::json::Value& value ){ + ext::vulkan::enums::Shader::type_t type; + std::string filename = ""; + std::string pipeline = ""; - Texture2D& texture = blitter.material.textures.emplace_back(); - enums::Filter::type_t filter = VK_FILTER_NEAREST; - /* - VkFormatProperties formatProperties; - vkGetPhysicalDeviceFormatProperties( device.physicalDevice, texture.format, &formatProperties ); - if ( formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT ) - filter = VK_FILTER_LINEAR; - */ - texture.sampler.descriptor.filter.min = filter; - texture.sampler.descriptor.filter.mag = filter; - texture.aliasAttachment(attachment); + if ( value.is() ) { + filename = value.as(); + auto split = uf::string::split( filename, "." ); + std::string extension = split.back(); split.pop_back(); + std::string sType = split.back(); + + if ( sType == "vert" ) type = ext::vulkan::enums::Shader::VERTEX; + else if ( sType == "frag" ) type = ext::vulkan::enums::Shader::FRAGMENT; + else if ( sType == "geom" ) type = ext::vulkan::enums::Shader::GEOMETRY; + else if ( sType == "comp" ) type = ext::vulkan::enums::Shader::COMPUTE; + } else { + filename = value["filename"].as(); + pipeline = value["pipeline"].as(); + std::string sType = value["type"].as(); + + if ( sType == "vertex" ) type = ext::vulkan::enums::Shader::VERTEX; + else if ( sType == "fragment" ) type = ext::vulkan::enums::Shader::FRAGMENT; + else if ( sType == "geometry" ) type = ext::vulkan::enums::Shader::GEOMETRY; + else if ( sType == "compute" ) type = ext::vulkan::enums::Shader::COMPUTE; + } + + blitter.material.attachShader( uf::io::root+filename, type, pipeline ); + }); + } else if ( ext::json::isObject( metadata["shaders"] ) ) { + ext::json::forEach( metadata["shaders"], [&]( const std::string& key, ext::json::Value& value ){ + ext::vulkan::enums::Shader::type_t type; + std::string filename = ""; + std::string pipeline = ""; + if ( value.is() ) { + filename = value.as(); + } else { + filename = value["filename"].as(); + pipeline = value["pipeline"].as(); + } + if ( key == "vertex" ) type = ext::vulkan::enums::Shader::VERTEX; + else if ( key == "fragment" ) type = ext::vulkan::enums::Shader::FRAGMENT; + else if ( key == "geometry" ) type = ext::vulkan::enums::Shader::GEOMETRY; + else if ( key == "compute" ) type = ext::vulkan::enums::Shader::COMPUTE; + + blitter.material.attachShader( uf::io::root+filename, type, pipeline ); + }); + } else if ( metadata["shaders"].is() && !metadata["shaders"].as() ) { + // do not attach if we're requesting no blitter shaders + blitter.process = false; + } else { + blitter.material.initializeShaders({ + {uf::io::root+"/shaders/display/renderTarget.vert.spv", ext::vulkan::enums::Shader::VERTEX}, + {uf::io::root+"/shaders/display/renderTarget.frag.spv", ext::vulkan::enums::Shader::FRAGMENT} + }); + } + if ( metadata["type"].as() == "svogi" ) { + auto& scene = uf::scene::getCurrentScene(); + + auto& shader = blitter.material.getShader("compute"); + struct SpecializationConstant { + uint32_t maxTextures = 512; + }; + auto& specializationConstants = shader.specializationConstants.get(); + + auto& metadata = scene.getComponent(); + size_t maxLights = metadata["system"]["config"]["engine"]["scenes"]["lights"]["max"].as(); + specializationConstants.maxTextures = metadata["system"]["config"]["engine"]["scenes"]["textures"]["max"].as(); + for ( auto& binding : shader.descriptorSetLayoutBindings ) { + if ( binding.descriptorCount > 1 ) + binding.descriptorCount = specializationConstants.maxTextures; + } + + std::vector lights(maxLights); + std::vector materials(specializationConstants.maxTextures); + std::vector textures(specializationConstants.maxTextures); + std::vector drawCalls(specializationConstants.maxTextures); + + for ( auto& material : materials ) material.colorBase = {0,0,0,0}; + + this->metadata["lightBufferIndex"] = blitter.initializeBuffer( + (void*) lights.data(), + lights.size() * sizeof(pod::Light::Storage), + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + ); + this->metadata["materialBufferIndex"] = blitter.initializeBuffer( + (void*) materials.data(), + materials.size() * sizeof(pod::Material::Storage), + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + ); + + this->metadata["textureBufferIndex"] = blitter.initializeBuffer( + (void*) textures.data(), + textures.size() * sizeof(pod::Texture::Storage), + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + ); + + this->metadata["drawCallBufferIndex"] = blitter.initializeBuffer( + (void*) drawCalls.data(), + drawCalls.size() * sizeof(pod::DrawCall::Storage), + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + ); + } else { + for ( auto& attachment : renderTarget.attachments ) { + if ( !(attachment.descriptor.usage & VK_IMAGE_USAGE_SAMPLED_BIT) ) continue; + + Texture2D& texture = blitter.material.textures.emplace_back(); + enums::Filter::type_t filter = VK_FILTER_NEAREST; + /* + VkFormatProperties formatProperties; + vkGetPhysicalDeviceFormatProperties( device.physicalDevice, texture.format, &formatProperties ); + if ( formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT ) + filter = VK_FILTER_LINEAR; + */ + texture.sampler.descriptor.filter.min = filter; + texture.sampler.descriptor.filter.mag = filter; + texture.aliasAttachment(attachment); + } } } } void ext::vulkan::RenderTargetRenderMode::tick() { ext::vulkan::RenderMode::tick(); + if ( metadata["type"].as() == "svogi" ) { + if ( ext::vulkan::states::resized ) { + renderTarget.initialize( *renderTarget.device ); + if ( blitter.process ) blitter.getPipeline().update( blitter ); + } + return; + } if ( ext::vulkan::states::resized ) { renderTarget.initialize( *renderTarget.device ); - /* - for ( auto& texture : blitter.material.textures ) { - texture.sampler.destroy(); - } - */ + // for ( auto& texture : blitter.material.textures ) texture.sampler.destroy(); blitter.material.textures.clear(); for ( auto& attachment : renderTarget.attachments ) { if ( !(attachment.descriptor.usage & VK_IMAGE_USAGE_SAMPLED_BIT) ) continue; Texture2D& texture = blitter.material.textures.emplace_back(); enums::Filter::type_t filter = VK_FILTER_NEAREST; - /* - VkFormatProperties formatProperties; - vkGetPhysicalDeviceFormatProperties( device->physicalDevice, texture.format, &formatProperties ); - if ( formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT ) - filter = VK_FILTER_LINEAR; - */ + texture.sampler.descriptor.filter.min = filter; texture.sampler.descriptor.filter.mag = filter; texture.aliasAttachment(attachment); } - if ( blitter.process ) { - blitter.getPipeline().update( blitter ); - } + if ( blitter.process ) blitter.getPipeline().update( blitter ); } } void ext::vulkan::RenderTargetRenderMode::destroy() { @@ -471,6 +575,9 @@ void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const std::vecto size_t subpasses = renderTarget.passes.size(); size_t currentPass = 0; + // pre-renderpass commands + if ( commandBufferCallbacks.count(CALLBACK_BEGIN) > 0 ) commandBufferCallbacks[CALLBACK_BEGIN]( commands[i] ); + vkCmdBeginRenderPass(commands[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdSetViewport(commands[i], 0, 1, &viewport); vkCmdSetScissor(commands[i], 0, 1, &scissor); @@ -481,9 +588,13 @@ void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const std::vecto ext::vulkan::GraphicDescriptor descriptor = bindGraphicDescriptor(graphic->descriptor, currentPass); graphic->record( commands[i], descriptor, currentPass, currentDraw++ ); } + if ( commandBufferCallbacks.count( currentPass ) > 0 ) commandBufferCallbacks[currentPass]( commands[i] ); if ( currentPass + 1 < subpasses ) vkCmdNextSubpass(commands[i], VK_SUBPASS_CONTENTS_INLINE); } vkCmdEndRenderPass(commands[i]); + + // post-renderpass commands + if ( commandBufferCallbacks.count(CALLBACK_END) > 0 ) commandBufferCallbacks[CALLBACK_END]( commands[i] ); } VK_CHECK_RESULT(vkEndCommandBuffer(commands[i])); diff --git a/engine/src/ext/vulkan/texture.cpp b/engine/src/ext/vulkan/texture.cpp index 124ce763..49e3537f 100644 --- a/engine/src/ext/vulkan/texture.cpp +++ b/engine/src/ext/vulkan/texture.cpp @@ -1,5 +1,5 @@ #if UF_USE_VULKAN - +#include #include #include #include @@ -372,13 +372,15 @@ void ext::vulkan::Texture::fromBuffers( if ( this->mips == 0 ) { this->mips = 1; - } else if ( this->depth == 1 ) { - this->mips = static_cast(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1; +// } else if ( this->depth == 1 ) { + } else { + // this->mips = static_cast(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1; + this->mips = static_cast(std::floor(std::log2(std::max(std::max(texWidth, texHeight),texDepth)))) + 1; VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(device.physicalDevice, format, &formatProperties); if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { this->mips = 1; - VK_VALIDATION_MESSAGE("Texture image format does not support linear blitting!"); + VK_VALIDATION_MESSAGE("Texture image format `" << format << "` does not support linear blitting!"); } } @@ -529,6 +531,10 @@ void ext::vulkan::Texture::aliasTexture( const Texture& texture ) { imageLayout = texture.imageLayout; deviceMemory = texture.deviceMemory; sampler = texture.sampler; + width = texture.width; + height = texture.height; + depth = texture.depth; + layers = texture.layers; this->updateDescriptors(); } @@ -596,7 +602,6 @@ void ext::vulkan::Texture::update( void* data, VkDeviceSize bufferSize, VkImageL VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange ); - // Copy mip levels from staging buffer vkCmdCopyBufferToImage( commandBuffer, @@ -606,9 +611,7 @@ void ext::vulkan::Texture::update( void* data, VkDeviceSize bufferSize, VkImageL 1, &bufferCopyRegion ); - if ( this->mips > 1 ) this->generateMipmaps(commandBuffer, layer); - setImageLayout( commandBuffer, image, @@ -616,6 +619,7 @@ void ext::vulkan::Texture::update( void* data, VkDeviceSize bufferSize, VkImageL targetImageLayout, subresourceRange ); + device.flushCommandBuffer(commandBuffer); // Clean up staging resources staging.destroy(); @@ -624,16 +628,17 @@ void ext::vulkan::Texture::update( void* data, VkDeviceSize bufferSize, VkImageL this->updateDescriptors(); } - void ext::vulkan::Texture::generateMipmaps( VkCommandBuffer commandBuffer, uint32_t layer ) { auto& device = *this->device; if ( this->mips <= 1 ) return; + bool blitting = true; VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(device.physicalDevice, format, &formatProperties); if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { - UF_EXCEPTION("texture image format does not support linear blitting!"); + UF_EXCEPTION("Texture image format `" + std::to_string(format) + "` does not support linear blitting!"); + blitting = false; } // base layer barrier @@ -662,28 +667,32 @@ void ext::vulkan::Texture::generateMipmaps( VkCommandBuffer commandBuffer, uint3 1, &barrier ); - // blit to current mip layer - VkImageBlit blit{}; - blit.srcOffsets[0] = { 0, 0, 0 }; - blit.srcOffsets[1] = { mipWidth, mipHeight, mipDepth }; - blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.srcSubresource.mipLevel = i - 1; - blit.srcSubresource.baseArrayLayer = layer; - blit.srcSubresource.layerCount = 1; - blit.dstOffsets[0] = { 0, 0, 0 }; - blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, mipDepth > 1 ? mipDepth / 2 : 1 }; - blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.dstSubresource.mipLevel = i; - blit.dstSubresource.baseArrayLayer = layer; - blit.dstSubresource.layerCount = 1; + if ( blitting ) { + // blit to current mip layer + VkImageBlit blit{}; + blit.srcOffsets[0] = { 0, 0, 0 }; + blit.srcOffsets[1] = { mipWidth, mipHeight, mipDepth }; + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.srcSubresource.mipLevel = i - 1; + blit.srcSubresource.baseArrayLayer = layer; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0] = { 0, 0, 0 }; + blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, mipDepth > 1 ? mipDepth / 2 : 1 }; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.mipLevel = i; + blit.dstSubresource.baseArrayLayer = layer; + blit.dstSubresource.layerCount = 1; - vkCmdBlitImage( - commandBuffer, - image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, &blit, - VK_FILTER_LINEAR - ); + vkCmdBlitImage( + commandBuffer, + image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &blit, + VK_FILTER_LINEAR + ); + } else { + + } // transition previous layer back barrier.subresourceRange.baseMipLevel = i - 1; diff --git a/engine/src/ext/vulkan/vulkan.cpp b/engine/src/ext/vulkan/vulkan.cpp index 1d8345cd..7aa01a65 100644 --- a/engine/src/ext/vulkan/vulkan.cpp +++ b/engine/src/ext/vulkan/vulkan.cpp @@ -217,7 +217,7 @@ void ext::vulkan::initialize() { }; Texture2D::empty.sampler.descriptor.filter.min = VK_FILTER_NEAREST; Texture2D::empty.sampler.descriptor.filter.mag = VK_FILTER_NEAREST; - Texture2D::empty.fromBuffers( (void*) &pixels[0], pixels.size(), ext::vulkan::enums::Format::R8G8B8A8_UNORM, 2, 2, ext::vulkan::device, VK_IMAGE_USAGE_SAMPLED_BIT ); + Texture2D::empty.fromBuffers( (void*) &pixels[0], pixels.size(), ext::vulkan::enums::Format::R8G8B8A8_UNORM, 2, 2, ext::vulkan::device, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL ); } { std::vector pixels = { @@ -229,7 +229,7 @@ void ext::vulkan::initialize() { }; Texture3D::empty.sampler.descriptor.filter.min = VK_FILTER_NEAREST; Texture3D::empty.sampler.descriptor.filter.mag = VK_FILTER_NEAREST; - Texture3D::empty.fromBuffers( (void*) &pixels[0], pixels.size(), ext::vulkan::enums::Format::R8G8B8A8_UNORM, 2, 2, 2, 1, ext::vulkan::device, VK_IMAGE_USAGE_SAMPLED_BIT ); + Texture3D::empty.fromBuffers( (void*) &pixels[0], pixels.size(), ext::vulkan::enums::Format::R8G8B8A8_UNORM, 2, 2, 2, 1, ext::vulkan::device, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL ); } { std::vector pixels = { @@ -253,7 +253,7 @@ void ext::vulkan::initialize() { }; TextureCube::empty.sampler.descriptor.filter.min = VK_FILTER_NEAREST; TextureCube::empty.sampler.descriptor.filter.mag = VK_FILTER_NEAREST; - TextureCube::empty.fromBuffers( (void*) &pixels[0], pixels.size(), ext::vulkan::enums::Format::R8G8B8A8_UNORM, 2, 2, 1, 6, ext::vulkan::device, VK_IMAGE_USAGE_SAMPLED_BIT ); + TextureCube::empty.fromBuffers( (void*) &pixels[0], pixels.size(), ext::vulkan::enums::Format::R8G8B8A8_UNORM, 2, 2, 1, 6, ext::vulkan::device, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL ); } for ( auto& renderMode : renderModes ) { if ( !renderMode ) continue; diff --git a/ext/behaviors/scene/behavior.cpp b/ext/behaviors/scene/behavior.cpp index fb4d1f17..0eec2fd1 100644 --- a/ext/behaviors/scene/behavior.cpp +++ b/ext/behaviors/scene/behavior.cpp @@ -121,11 +121,11 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) { uf::hooks.call("window:Mouse.CursorVisibility", payload); uf::hooks.call("window:Mouse.Lock"); } - + auto& sceneTextures = this->getComponent(); // initialize perlin noise #if UF_USE_VULKAN { - auto& texture = this->getComponent(); + auto& texture = sceneTextures.noise; //this->getComponent(); texture.sampler.descriptor.addressMode = { uf::renderer::enums::AddressMode::MIRRORED_REPEAT, uf::renderer::enums::AddressMode::MIRRORED_REPEAT, @@ -167,6 +167,8 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) { } texture.fromBuffers( (void*) pixels.data(), pixels.size(), uf::renderer::enums::Format::R8_UNORM, size.x, size.y, size.z, 1 ); } + // initialize voxel map + // initialize cubemap { std::vector filenames = { @@ -181,7 +183,7 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) { std::vector images(filenames.size()); pod::Vector2ui size = {0,0}; - auto& texture = this->getComponent(); + auto& texture = sceneTextures.skybox; //this->getComponent(); for ( size_t i = 0; i < filenames.size(); ++i ) { auto& filename = filenames[i]; auto& image = images[i]; @@ -361,327 +363,7 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) { if ( !metadata.light.updateThreshold ) metadata.light.updateThreshold = metadataJson["system"]["config"]["engine"]["scenes"]["lights"]["update threshold"].as(); #endif /* Update lights */ if ( metadata.light.enabled ) { - auto& scene = uf::scene::getCurrentScene(); - auto& controller = scene.getController(); - auto& camera = controller.getComponent(); - auto& renderMode = uf::renderer::getRenderMode("", true); - auto& controllerMetadata = controller.getComponent(); - auto& controllerTransform = controller.getComponent>(); - std::vector blitters = renderMode.getBlitters(); - - #if UF_USE_OPENGL - struct LightInfo { - uf::Entity* entity = NULL; - pod::Vector4f position = {0,0,0,1}; - pod::Vector4f color = {0,0,0,1}; - float distance = 0; - float power = 0; - }; - std::vector entities; - auto graph = uf::scene::generateGraph(); - for ( auto entity : graph ) { - if ( entity == &controller || entity == this ) continue; - // if ( entity->getName() != "Light" && !entity->hasComponent() ) continue; - if ( !entity->hasComponent() ) continue; - // if ( !entity->hasBehavior(pod::Behavior{.type = ext::LightBehavior::type}) ) continue; - auto& metadata = entity->getComponent(); - if ( metadata.power <= 0 ) continue; - LightInfo& info = entities.emplace_back(); - auto& transform = entity->getComponent>(); - auto flatten = uf::transform::flatten( transform ); - info.entity = entity; - info.position = flatten.position; - info.position.w = 1; - info.color = metadata.color; - info.color.w = 1; - info.distance = uf::vector::magnitude( uf::vector::subtract( flatten.position, controllerTransform.position ) ); - info.power = metadata.power; - } - std::sort( entities.begin(), entities.end(), [&]( LightInfo& l, LightInfo& r ){ - return l.distance < r.distance; - }); - - static GLint glMaxLights = 0; - if ( !glMaxLights ) glGetIntegerv(GL_MAX_LIGHTS, &glMaxLights); - metadata.max.lights = std::min( (size_t) glMaxLights, metadata.max.lights ); - - // add lighting - { - size_t i = 0; - for ( ; i < entities.size() && i < metadata.max.lights; ++i ) { - auto& info = entities[i]; - uf::Entity* entity = info.entity; - GLenum target = GL_LIGHT0+i; - GL_ERROR_CHECK(glEnable(target)); - GL_ERROR_CHECK(glLightfv(target, GL_AMBIENT, &metadata.light.ambient[0])); - GL_ERROR_CHECK(glLightfv(target, GL_SPECULAR, &metadata.light.specular[0])); - GL_ERROR_CHECK(glLightfv(target, GL_DIFFUSE, &info.color[0])); - GL_ERROR_CHECK(glLightfv(target, GL_POSITION, &info.position[0])); - GL_ERROR_CHECK(glLightf(target, GL_CONSTANT_ATTENUATION, 0.0f)); - GL_ERROR_CHECK(glLightf(target, GL_LINEAR_ATTENUATION, 0)); - GL_ERROR_CHECK(glLightf(target, GL_QUADRATIC_ATTENUATION, 1.0f / info.power)); - } - for ( ; i < metadata.max.lights; ++i ) GL_ERROR_CHECK(glDisable(GL_LIGHT0+i)); - } - #elif UF_USE_VULKAN - size_t maxTextures = metadata.max.textures; - struct UniformDescriptor { - struct Matrices { - alignas(16) pod::Matrix4f view[2]; - alignas(16) pod::Matrix4f projection[2]; - alignas(16) pod::Matrix4f iView[2]; - alignas(16) pod::Matrix4f iProjection[2]; - alignas(16) pod::Matrix4f iProjectionView[2]; - } matrices; - struct Mode { - alignas(8) pod::Vector2ui type; - alignas(8) pod::Vector2ui padding; - alignas(16) pod::Vector4f parameters; - } mode; - struct { - alignas(16) pod::Vector4f color; // w: stepScale - alignas(16) pod::Vector4f offset; // w: densityScale - alignas(4) float densityThreshold; - alignas(4) float densityMultiplier; - - alignas(4) float absorbtion; - alignas(4) float padding1; - - alignas(8) pod::Vector2f range; - alignas(4) float padding2; - alignas(4) float padding3; - } fog; - struct { - alignas(4) uint32_t lights = 0; - alignas(4) uint32_t materials = 0; - alignas(4) uint32_t textures = 0; - alignas(4) uint32_t drawCalls = 0; - } lengths; - alignas(16) pod::Vector4f ambient; - // alignas(16) pod::Vector4f position; - - alignas(4) uint32_t msaa; - alignas(4) uint32_t poissonSamples; - alignas(4) uint32_t padding1; - alignas(4) uint32_t padding2; - }; - struct SpecializationConstant { - uint32_t maxTextures = 512; - } specializationConstants; - specializationConstants.maxTextures = maxTextures; - - struct LightInfo { - uf::Entity* entity = NULL; - pod::Vector4f color = {0,0,0,0}; - pod::Vector3f position = {0,0,0}; - float power = 0; - float distance = 0; - float bias = 0; - bool shadows = false; - size_t type = 0; - }; - std::vector entities; - std::vector graphs; - auto graph = uf::scene::generateGraph(); - for ( auto entity : graph ) { - if ( entity == &controller || entity == this ) continue; - if ( entity->hasComponent() ) graphs.emplace_back(&entity->getComponent()); - // if ( entity->getName() != "Light" && !entity->hasComponent() ) continue; - if ( !entity->hasComponent() ) continue; - // if ( !entity->hasBehavior(pod::Behavior{.type = ext::LightBehavior::type}) ) continue; - auto& metadata = entity->getComponent(); - if ( entity->hasComponent() ) { - auto& renderMode = entity->getComponent(); - if ( metadata.renderer.mode == "in-range" ) renderMode.execute = false; - } - if ( metadata.power <= 0 ) continue; - LightInfo& info = entities.emplace_back(); - auto& transform = entity->getComponent>(); - auto flatten = uf::transform::flatten( transform ); - info.entity = entity; - info.position = flatten.position; - info.color = metadata.color; - info.color.w = metadata.power; - info.distance = uf::vector::magnitude( uf::vector::subtract( flatten.position, controllerTransform.position ) ); - info.shadows = metadata.shadows; - info.bias = metadata.bias; - info.type = metadata.type; - } - std::sort( entities.begin(), entities.end(), [&]( LightInfo& l, LightInfo& r ){ - return l.distance < r.distance; - }); - - int shadowSamples = metadata.light.shadowSamples; - int shadowThreshold = metadata.light.shadowThreshold; - if ( shadowSamples <= 0 ) shadowSamples = 16; - if ( shadowThreshold <= 0 ) shadowThreshold = std::numeric_limits::max(); - { - std::vector scratch; - scratch.reserve(entities.size()); - for ( size_t i = 0; i < entities.size(); ++i ) { - auto& info = entities[i]; - if ( info.shadows && --shadowThreshold <= 0 ) info.shadows = false; - scratch.emplace_back(info); - } - entities = scratch; - } - - for ( auto* blitter : blitters ) { - auto& graphic = *blitter; - auto& shader = graphic.material.getShader("fragment"); - auto& uniform = shader.getUniform("UBO"); - uint8_t* buffer = (uint8_t*) (void*) uniform; - - UniformDescriptor* uniforms = (UniformDescriptor*) buffer; - - for ( std::size_t i = 0; i < 2; ++i ) { - uniforms->matrices.view[i] = camera.getView( i ); - uniforms->matrices.projection[i] = camera.getProjection( i ); - uniforms->matrices.iView[i] = uf::matrix::inverse( uniforms->matrices.view[i] ); - uniforms->matrices.iProjection[i] = uf::matrix::inverse( uniforms->matrices.projection[i] ); - uniforms->matrices.iProjectionView[i] = uf::matrix::inverse( uniforms->matrices.projection[i] * uniforms->matrices.view[i] ); - } - - uniforms->ambient = metadata.light.ambient; - uniforms->msaa = ext::vulkan::settings::msaa; - uniforms->poissonSamples = shadowSamples; - - uniforms->fog.color = metadata.fog.color; - uniforms->fog.color.w = metadata.fog.stepScale; - - float timescale = metadata.fog.density.timescale; - uniforms->fog.offset = metadata.fog.density.offset * uf::physics::time::current * timescale; - uniforms->fog.offset.w = metadata.fog.density.scale; - - uniforms->fog.densityThreshold = metadata.fog.density.threshold; - uniforms->fog.densityMultiplier = metadata.fog.density.multiplier; - uniforms->fog.absorbtion = metadata.fog.absorbtion; - - uniforms->fog.range = metadata.fog.range; - - uniforms->mode.type.x = metadataJson["system"]["renderer"]["shader"]["mode"].as(); - uniforms->mode.type.y = metadataJson["system"]["renderer"]["shader"]["scalar"].as(); - uniforms->mode.parameters = uf::vector::decode( metadataJson["system"]["renderer"]["shader"]["parameters"], uniforms->mode.parameters ); - if ( metadataJson["system"]["renderer"]["shader"]["parameters"][3].as() == "time" ) { - uniforms->mode.parameters.w = uf::physics::time::current; - } - - std::vector previousTextures; - for ( auto& texture : graphic.material.textures ) previousTextures.emplace_back(texture.image); - - graphic.material.textures.clear(); - graphic.material.textures.emplace_back().aliasTexture(this->getComponent()); - graphic.material.textures.emplace_back().aliasTexture(this->getComponent()); - - size_t updateThreshold = metadata.light.updateThreshold; - size_t textureSlot = 0; - - std::vector lights; - lights.reserve( metadata.max.lights ); - - std::vector materials; - materials.reserve(maxTextures); - materials.emplace_back().colorBase = {0,0,0,0}; - - std::vector textures; - textures.reserve(maxTextures); - - std::vector drawCalls; - drawCalls.reserve(maxTextures); - - // add materials - for ( auto* entity : graphs ) { - auto& graph = *entity; - - drawCalls.emplace_back(pod::DrawCall::Storage{ - materials.size(), - graph.materials.size(), - textures.size(), - graph.textures.size() - }); - - for ( auto& material : graph.materials ) materials.emplace_back( material.storage ); - for ( auto& texture : graph.textures ) textures.emplace_back( texture.storage ); - - for ( auto& texture : graph.textures ) { - if ( !texture.bind ) continue; - graphic.material.textures.emplace_back().aliasTexture(texture.texture); - ++textureSlot; - } - } - - uniforms->lengths.materials = std::min( materials.size(), maxTextures ); - uniforms->lengths.textures = std::min( textures.size(), maxTextures ); - uniforms->lengths.drawCalls = std::min( drawCalls.size(), maxTextures ); - // add lighting - for ( size_t i = 0; i < entities.size() && lights.size() < metadata.max.lights; ++i ) { - auto& info = entities[i]; - uf::Entity* entity = info.entity; - - auto& transform = entity->getComponent>(); - auto& metadata = entity->getComponent(); - auto& camera = entity->getComponent(); - metadata.renderer.rendered = true; - - pod::Light::Storage light; - light.position = info.position; - - light.color = info.color; - light.type = info.type; - light.mapIndex = -1; - - light.depthBias = info.bias; - if ( info.shadows && entity->hasComponent() ) { - auto& renderMode = entity->getComponent(); - if ( metadata.renderer.mode == "in-range" && --updateThreshold > 0 ) { - renderMode.execute = true; - } - size_t view = 0; - for ( auto& attachment : renderMode.renderTarget.attachments ) { - if ( !(attachment.descriptor.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ) continue; - if ( attachment.descriptor.layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ) continue; - - graphic.material.textures.emplace_back().aliasAttachment(attachment); - - light.mapIndex = textureSlot++; - light.view = camera.getView(view); - light.projection = camera.getProjection(view); - lights.emplace_back(light); - - ++view; - } - light.mapIndex = -1; - } else { - lights.emplace_back(light); - } - } - { - uniforms->lengths.lights = std::min( lights.size(), metadata.max.lights ); - bool shouldUpdate = graphic.material.textures.size() != previousTextures.size(); - for ( size_t i = 0; !shouldUpdate && i < previousTextures.size() && i < graphic.material.textures.size(); ++i ) { - if ( previousTextures[i] != graphic.material.textures[i].image ) - shouldUpdate = true; - } - - size_t lightBufferIndex = renderMode.metadata["lightBufferIndex"].as(); - graphic.updateBuffer( (void*) lights.data(), uniforms->lengths.lights * sizeof(pod::Light::Storage), lightBufferIndex, false ); - - if ( shouldUpdate ) { - size_t materialBufferIndex = renderMode.metadata["materialBufferIndex"].as(); - graphic.updateBuffer( (void*) materials.data(), uniforms->lengths.materials * sizeof(pod::Material::Storage), materialBufferIndex, false ); - - size_t textureBufferIndex = renderMode.metadata["textureBufferIndex"].as(); - graphic.updateBuffer( (void*) textures.data(), uniforms->lengths.textures * sizeof(pod::Texture::Storage), textureBufferIndex, false ); - - size_t drawCallBufferIndex = renderMode.metadata["drawCallBufferIndex"].as(); - graphic.updateBuffer( (void*) drawCalls.data(), uniforms->lengths.drawCalls * sizeof(pod::DrawCall::Storage), drawCallBufferIndex, false ); - - graphic.updatePipelines(); - } - shader.updateUniform( "UBO", uniform ); - } - } - #endif + ext::ExtSceneBehavior::bindBuffers( *this ); } #if UF_ENTITY_METADATA_USE_JSON @@ -690,4 +372,362 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) { } void ext::ExtSceneBehavior::render( uf::Object& self ) {} void ext::ExtSceneBehavior::destroy( uf::Object& self ) {} + +void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, const std::string& renderModeName, bool isCompute ) { + auto& controller = this->getController(); + auto& camera = controller.getComponent(); + auto& controllerMetadata = controller.getComponent(); + auto& controllerTransform = controller.getComponent>(); + auto& metadata = this->getComponent(); + auto& metadataJson = this->getComponent(); + + auto& renderMode = uf::renderer::getRenderMode(renderModeName, true); + std::vector blitters = renderMode.getBlitters(); + +#if UF_USE_OPENGL + struct LightInfo { + uf::Entity* entity = NULL; + pod::Vector4f position = {0,0,0,1}; + pod::Vector4f color = {0,0,0,1}; + float distance = 0; + float power = 0; + }; + std::vector entities; + auto graph = uf::scene::generateGraph(); + for ( auto entity : graph ) { + if ( entity == &controller || entity == this ) continue; + // if ( entity->getName() != "Light" && !entity->hasComponent() ) continue; + if ( !entity->hasComponent() ) continue; + // if ( !entity->hasBehavior(pod::Behavior{.type = ext::LightBehavior::type}) ) continue; + auto& metadata = entity->getComponent(); + if ( metadata.power <= 0 ) continue; + LightInfo& info = entities.emplace_back(); + auto& transform = entity->getComponent>(); + auto flatten = uf::transform::flatten( transform ); + info.entity = entity; + info.position = flatten.position; + info.position.w = 1; + info.color = metadata.color; + info.color.w = 1; + info.distance = uf::vector::magnitude( uf::vector::subtract( flatten.position, controllerTransform.position ) ); + info.power = metadata.power; + } + std::sort( entities.begin(), entities.end(), [&]( LightInfo& l, LightInfo& r ){ + return l.distance < r.distance; + }); + + static GLint glMaxLights = 0; + if ( !glMaxLights ) glGetIntegerv(GL_MAX_LIGHTS, &glMaxLights); + metadata.max.lights = std::min( (size_t) glMaxLights, metadata.max.lights ); + + // add lighting + { + size_t i = 0; + for ( ; i < entities.size() && i < metadata.max.lights; ++i ) { + auto& info = entities[i]; + uf::Entity* entity = info.entity; + GLenum target = GL_LIGHT0+i; + GL_ERROR_CHECK(glEnable(target)); + GL_ERROR_CHECK(glLightfv(target, GL_AMBIENT, &metadata.light.ambient[0])); + GL_ERROR_CHECK(glLightfv(target, GL_SPECULAR, &metadata.light.specular[0])); + GL_ERROR_CHECK(glLightfv(target, GL_DIFFUSE, &info.color[0])); + GL_ERROR_CHECK(glLightfv(target, GL_POSITION, &info.position[0])); + GL_ERROR_CHECK(glLightf(target, GL_CONSTANT_ATTENUATION, 0.0f)); + GL_ERROR_CHECK(glLightf(target, GL_LINEAR_ATTENUATION, 0)); + GL_ERROR_CHECK(glLightf(target, GL_QUADRATIC_ATTENUATION, 1.0f / info.power)); + } + for ( ; i < metadata.max.lights; ++i ) GL_ERROR_CHECK(glDisable(GL_LIGHT0+i)); + } +#elif UF_USE_VULKAN + size_t maxTextures = metadata.max.textures; + struct UniformDescriptor { + struct Matrices { + alignas(16) pod::Matrix4f view[2]; + alignas(16) pod::Matrix4f projection[2]; + alignas(16) pod::Matrix4f iView[2]; + alignas(16) pod::Matrix4f iProjection[2]; + alignas(16) pod::Matrix4f iProjectionView[2]; + alignas(16) pod::Matrix4f ortho; + } matrices; + struct Mode { + alignas(8) pod::Vector2ui type; + alignas(8) pod::Vector2ui padding; + alignas(16) pod::Vector4f parameters; + } mode; + struct { + alignas(16) pod::Vector4f color; // w: stepScale + alignas(16) pod::Vector4f offset; // w: densityScale + alignas(4) float densityThreshold; + alignas(4) float densityMultiplier; + + alignas(4) float absorbtion; + alignas(4) float padding1; + + alignas(8) pod::Vector2f range; + alignas(4) float padding2; + alignas(4) float padding3; + } fog; + struct { + alignas(4) uint32_t lights = 0; + alignas(4) uint32_t materials = 0; + alignas(4) uint32_t textures = 0; + alignas(4) uint32_t drawCalls = 0; + } lengths; + alignas(16) pod::Vector4f ambient; + + alignas(4) uint32_t msaa; + alignas(4) uint32_t poissonSamples; + alignas(4) uint32_t padding1; + alignas(4) uint32_t padding2; + }; + struct SpecializationConstant { + uint32_t maxTextures = 512; + } specializationConstants; + specializationConstants.maxTextures = maxTextures; + + struct LightInfo { + uf::Entity* entity = NULL; + pod::Vector4f color = {0,0,0,0}; + pod::Vector3f position = {0,0,0}; + float power = 0; + float distance = 0; + float bias = 0; + bool shadows = false; + size_t type = 0; + }; + std::vector entities; + std::vector graphs; + + auto graph = uf::scene::generateGraph(); + for ( auto entity : graph ) { + if ( entity == &controller || entity == this ) continue; + if ( entity->hasComponent() ) graphs.emplace_back(&entity->getComponent()); + if ( !entity->hasComponent() ) continue; + auto& metadata = entity->getComponent(); + if ( entity->hasComponent() ) { + auto& renderMode = entity->getComponent(); + if ( metadata.renderer.mode == "in-range" ) renderMode.execute = false; + } + if ( metadata.power <= 0 ) continue; + LightInfo& info = entities.emplace_back(); + auto& transform = entity->getComponent>(); + auto flatten = uf::transform::flatten( transform ); + info.entity = entity; + info.position = flatten.position; + info.color = metadata.color; + info.color.w = metadata.power; + info.distance = uf::vector::magnitude( uf::vector::subtract( flatten.position, controllerTransform.position ) ); + info.shadows = metadata.shadows; + info.bias = metadata.bias; + info.type = metadata.type; + } + std::sort( entities.begin(), entities.end(), [&]( LightInfo& l, LightInfo& r ){ + return l.distance < r.distance; + }); + + int shadowSamples = metadata.light.shadowSamples; + int shadowThreshold = metadata.light.shadowThreshold; + if ( shadowSamples <= 0 ) shadowSamples = 16; + if ( shadowThreshold <= 0 ) shadowThreshold = std::numeric_limits::max(); + { + std::vector scratch; + scratch.reserve(entities.size()); + for ( size_t i = 0; i < entities.size(); ++i ) { + auto& info = entities[i]; + if ( info.shadows && --shadowThreshold <= 0 ) info.shadows = false; + scratch.emplace_back(info); + } + entities = scratch; + } + auto& sceneTextures = this->getComponent(); + for ( auto* blitter : blitters ) { + auto& graphic = *blitter; + if ( !graphic.initialized ) continue; + + auto& shader = graphic.material.getShader(isCompute ? "compute" : "fragment"); + auto& uniform = shader.getUniform("UBO"); + uint8_t* buffer = (uint8_t*) (void*) uniform; + + UniformDescriptor* uniforms = (UniformDescriptor*) buffer; + + for ( std::size_t i = 0; i < 2; ++i ) { + uniforms->matrices.view[i] = camera.getView( i ); + uniforms->matrices.projection[i] = camera.getProjection( i ); + uniforms->matrices.iView[i] = uf::matrix::inverse( uniforms->matrices.view[i] ); + uniforms->matrices.iProjection[i] = uf::matrix::inverse( uniforms->matrices.projection[i] ); + uniforms->matrices.iProjectionView[i] = uf::matrix::inverse( uniforms->matrices.projection[i] * uniforms->matrices.view[i] ); + } + + uniforms->ambient = metadata.light.ambient; + uniforms->msaa = ext::vulkan::settings::msaa; + uniforms->poissonSamples = shadowSamples; + + uniforms->fog.color = metadata.fog.color; + uniforms->fog.color.w = metadata.fog.stepScale; + + float timescale = metadata.fog.density.timescale; + uniforms->fog.offset = metadata.fog.density.offset * uf::physics::time::current * timescale; + uniforms->fog.offset.w = metadata.fog.density.scale; + + uniforms->fog.densityThreshold = metadata.fog.density.threshold; + uniforms->fog.densityMultiplier = metadata.fog.density.multiplier; + uniforms->fog.absorbtion = metadata.fog.absorbtion; + + uniforms->fog.range = metadata.fog.range; + + uniforms->mode.type.x = metadataJson["system"]["renderer"]["shader"]["mode"].as(); + uniforms->mode.type.y = metadataJson["system"]["renderer"]["shader"]["scalar"].as(); + uniforms->mode.parameters = uf::vector::decode( metadataJson["system"]["renderer"]["shader"]["parameters"], uniforms->mode.parameters ); + if ( metadataJson["system"]["renderer"]["shader"]["parameters"][3].as() == "time" ) { + uniforms->mode.parameters.w = uf::physics::time::current; + } + + std::vector previousTextures; + for ( auto& texture : graphic.material.textures ) previousTextures.emplace_back(texture.image); + + graphic.material.textures.clear(); + if ( uf::renderer::settings::experimental::deferredMode == "svogi" ) { + graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.id); //this->getComponent()); + graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.normal); //this->getComponent()); + graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.uv); //this->getComponent()); + graphic.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.albedo); //this->getComponent()); + } + + graphic.material.textures.emplace_back().aliasTexture(sceneTextures.noise); //this->getComponent()); + graphic.material.textures.emplace_back().aliasTexture(sceneTextures.skybox); //this->getComponent()); + + size_t updateThreshold = metadata.light.updateThreshold; + size_t textureSlot = 0; + + std::vector lights; + lights.reserve( metadata.max.lights ); + + std::vector materials; + materials.reserve(maxTextures); + materials.emplace_back().colorBase = {0,0,0,0}; + + std::vector textures; + textures.reserve(maxTextures); + + std::vector drawCalls; + drawCalls.reserve(maxTextures); + + // add materials + pod::Vector3f min = { std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; + pod::Vector3f max = { -std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max() }; + for ( auto* g : graphs ) { + auto& graph = *g; + + drawCalls.emplace_back(pod::DrawCall::Storage{ + materials.size(), + graph.materials.size(), + textures.size(), + graph.textures.size() + }); + + for ( auto& material : graph.materials ) materials.emplace_back( material.storage ); + for ( auto& texture : graph.textures ) textures.emplace_back( texture.storage ); + + for ( auto& texture : graph.textures ) { + if ( !texture.bind ) continue; + graphic.material.textures.emplace_back().aliasTexture(texture.texture); + ++textureSlot; + } + + // calculate extents + pod::Vector3f graphMin = uf::vector::decode( graph.metadata["extents"]["min"], pod::Vector3f{} ); + pod::Vector3f graphMax = uf::vector::decode( graph.metadata["extents"]["max"], pod::Vector3f{} ); + + min.x = std::min( min.x, graphMin.x ); + min.y = std::min( min.y, graphMin.y ); + min.z = std::min( min.z, graphMin.z ); + + max.x = std::max( max.x, graphMax.x ); + max.y = std::max( max.y, graphMax.y ); + max.z = std::max( max.z, graphMax.z ); + } + + min.x += floor(controllerTransform.position.x); + min.y -= floor(controllerTransform.position.y); + min.z -= floor(controllerTransform.position.z); + + max.x += floor(controllerTransform.position.x); + max.y -= floor(controllerTransform.position.y); + max.z -= floor(controllerTransform.position.z); + + uniforms->matrices.ortho = /*uf::matrix::translate( uf::matrix::identity(), controllerTransform.position ) **/ uf::matrix::ortho( min.x, max.x, min.y, max.y, min.z, max.z ); + + uniforms->lengths.materials = std::min( materials.size(), maxTextures ); + uniforms->lengths.textures = std::min( textures.size(), maxTextures ); + uniforms->lengths.drawCalls = std::min( drawCalls.size(), maxTextures ); + // add lighting + for ( size_t i = 0; i < entities.size() && lights.size() < metadata.max.lights; ++i ) { + auto& info = entities[i]; + uf::Entity* entity = info.entity; + + auto& transform = entity->getComponent>(); + auto& metadata = entity->getComponent(); + auto& camera = entity->getComponent(); + metadata.renderer.rendered = true; + + pod::Light::Storage light; + light.position = info.position; + + light.color = info.color; + light.type = info.type; + light.mapIndex = -1; + + light.depthBias = info.bias; + if ( info.shadows && entity->hasComponent() ) { + auto& renderMode = entity->getComponent(); + if ( metadata.renderer.mode == "in-range" && --updateThreshold > 0 ) { + renderMode.execute = true; + } + size_t view = 0; + for ( auto& attachment : renderMode.renderTarget.attachments ) { + if ( !(attachment.descriptor.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ) continue; + if ( attachment.descriptor.layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ) continue; + + graphic.material.textures.emplace_back().aliasAttachment(attachment); + + light.mapIndex = textureSlot++; + light.view = camera.getView(view); + light.projection = camera.getProjection(view); + lights.emplace_back(light); + + ++view; + } + light.mapIndex = -1; + } else { + lights.emplace_back(light); + } + } + { + uniforms->lengths.lights = std::min( lights.size(), metadata.max.lights ); + bool shouldUpdate = graphic.material.textures.size() != previousTextures.size(); + for ( size_t i = 0; !shouldUpdate && i < previousTextures.size() && i < graphic.material.textures.size(); ++i ) { + if ( previousTextures[i] != graphic.material.textures[i].image ) + shouldUpdate = true; + } + + size_t lightBufferIndex = renderMode.metadata["lightBufferIndex"].as(); + graphic.updateBuffer( (void*) lights.data(), uniforms->lengths.lights * sizeof(pod::Light::Storage), lightBufferIndex, false ); + + if ( shouldUpdate ) { + size_t materialBufferIndex = renderMode.metadata["materialBufferIndex"].as(); + graphic.updateBuffer( (void*) materials.data(), uniforms->lengths.materials * sizeof(pod::Material::Storage), materialBufferIndex, false ); + + size_t textureBufferIndex = renderMode.metadata["textureBufferIndex"].as(); + graphic.updateBuffer( (void*) textures.data(), uniforms->lengths.textures * sizeof(pod::Texture::Storage), textureBufferIndex, false ); + + size_t drawCallBufferIndex = renderMode.metadata["drawCallBufferIndex"].as(); + graphic.updateBuffer( (void*) drawCalls.data(), uniforms->lengths.drawCalls * sizeof(pod::DrawCall::Storage), drawCallBufferIndex, false ); + + graphic.updatePipelines(); + } + shader.updateUniform( "UBO", uniform ); + } + } +#endif +} #undef this \ No newline at end of file diff --git a/ext/behaviors/scene/behavior.h b/ext/behaviors/scene/behavior.h index cae95a5c..467f410b 100644 --- a/ext/behaviors/scene/behavior.h +++ b/ext/behaviors/scene/behavior.h @@ -14,6 +14,7 @@ namespace ext { void tick( uf::Object& ); void render( uf::Object& ); void destroy( uf::Object& ); + struct Metadata { struct { size_t textures = 256; @@ -44,5 +45,7 @@ namespace ext { std::function serialize; std::function deserialize; }; + + void bindBuffers( uf::Object&, const std::string& = "", bool = false ); } } \ No newline at end of file diff --git a/ext/behaviors/voxelizer/behavior.cpp b/ext/behaviors/voxelizer/behavior.cpp new file mode 100644 index 00000000..468d18bd --- /dev/null +++ b/ext/behaviors/voxelizer/behavior.cpp @@ -0,0 +1,149 @@ +#include "behavior.h" + +#include + +#include +#include +#include +#include +#include + +#include + +#include "../light/behavior.h" +#include "../scene/behavior.h" + +#define COMP_SHADER_USED 1 + +UF_BEHAVIOR_REGISTER_CPP(ext::VoxelizerBehavior) +#define this (&self) +void ext::VoxelizerBehavior::initialize( uf::Object& self ) { +#if UF_USE_VULKAN + auto& metadata = this->getComponent(); + auto& sceneTextures = this->getComponent(); + // initialize voxel map + { + if ( metadata.voxelSize.x == 0 ) metadata.voxelSize.x = 256; + if ( metadata.voxelSize.y == 0 ) metadata.voxelSize.y = 256; + if ( metadata.voxelSize.z == 0 ) metadata.voxelSize.z = 256; + + std::vector empty(metadata.voxelSize.x * metadata.voxelSize.y * metadata.voxelSize.z * sizeof(uint8_t) * 4); + + sceneTextures.voxels.id.sampler.descriptor.filter.min = VK_FILTER_NEAREST; + sceneTextures.voxels.id.sampler.descriptor.filter.mag = VK_FILTER_NEAREST; + + sceneTextures.voxels.id.fromBuffers( (void*) empty.data(), empty.size(), uf::renderer::enums::Format::R16G16_UINT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL ); + sceneTextures.voxels.normal.fromBuffers( (void*) empty.data(), empty.size(), uf::renderer::enums::Format::R16G16_SFLOAT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL ); + sceneTextures.voxels.uv.fromBuffers( (void*) empty.data(), empty.size(), uf::renderer::enums::Format::R16G16_SFLOAT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL ); + sceneTextures.voxels.albedo.fromBuffers( (void*) empty.data(), empty.size(), uf::renderer::enums::Format::R8G8B8A8_UNORM, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL ); + } + // initialize render mode + { + // if ( metadata.fragmentSize.x == 0 ) metadata.fragmentSize.x = metadata.voxelSize.x * 2; + // if ( metadata.fragmentSize.y == 0 ) metadata.fragmentSize.y = metadata.voxelSize.y * 2; + + auto& renderMode = this->getComponent(); + metadata.renderModeName = "SVOGI:" + std::to_string((int) this->getUid()); + uf::renderer::addRenderMode( &renderMode, metadata.renderModeName ); + renderMode.metadata["type"] = "svogi"; + renderMode.metadata["samples"] = 1; + + renderMode.blitter.device = &ext::vulkan::device; + renderMode.width = metadata.fragmentSize.x; + renderMode.height = metadata.fragmentSize.y; + #if COMP_SHADER_USED + renderMode.metadata["shaders"]["compute"] = "/shaders/display/svogi.comp.spv"; + renderMode.blitter.descriptor.renderMode = metadata.renderModeName; + renderMode.blitter.descriptor.subpass = -1; + renderMode.blitter.process = true; + #else + renderMode.metadata["shaders"] = false; + renderMode.blitter.process = false; + #endif + + renderMode.blitter.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.id); //this->getComponent()); + renderMode.blitter.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.normal); //this->getComponent()); + renderMode.blitter.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.uv); //this->getComponent()); + renderMode.blitter.material.textures.emplace_back().aliasTexture(sceneTextures.voxels.albedo); //this->getComponent()); + renderMode.blitter.material.textures.emplace_back().aliasTexture(sceneTextures.noise); //this->getComponent()); + renderMode.blitter.material.textures.emplace_back().aliasTexture(sceneTextures.skybox); //this->getComponent()); + + renderMode.bindCallback( renderMode.CALLBACK_BEGIN, [&]( VkCommandBuffer commandBuffer ){ + // clear textures + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.baseArrayLayer = 0; + subresourceRange.levelCount = 1; + subresourceRange.layerCount = 1; + + VkClearColorValue clearColor = { 0.0, 0.0, 0.0, 0.0 }; + vkCmdClearColorImage( commandBuffer, sceneTextures.voxels.id.image, sceneTextures.voxels.id.imageLayout, &clearColor, 1, &subresourceRange ); + vkCmdClearColorImage( commandBuffer, sceneTextures.voxels.normal.image, sceneTextures.voxels.normal.imageLayout, &clearColor, 1, &subresourceRange ); + vkCmdClearColorImage( commandBuffer, sceneTextures.voxels.uv.image, sceneTextures.voxels.uv.imageLayout, &clearColor, 1, &subresourceRange ); + vkCmdClearColorImage( commandBuffer, sceneTextures.voxels.albedo.image, sceneTextures.voxels.albedo.imageLayout, &clearColor, 1, &subresourceRange ); + }); + renderMode.bindCallback( renderMode.CALLBACK_END, [&]( VkCommandBuffer commandBuffer ){ + // parse voxel lighting + #if COMP_SHADER_USED + if ( renderMode.blitter.initialized ) { + auto& pipeline = renderMode.blitter.getPipeline(); + pipeline.record(renderMode.blitter, commandBuffer); + vkCmdDispatch(commandBuffer, metadata.voxelSize.x / metadata.dispatchSize.x, metadata.voxelSize.y / metadata.dispatchSize.y, metadata.voxelSize.z / metadata.dispatchSize.z); + } + #endif + // generate mipmaps + VkImageSubresourceRange subresourceRange = {}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.baseArrayLayer = 0; + subresourceRange.levelCount = sceneTextures.voxels.albedo.mips; + subresourceRange.layerCount = 1; + + sceneTextures.voxels.albedo.setImageLayout( + commandBuffer, + sceneTextures.voxels.albedo.image, + sceneTextures.voxels.albedo.imageLayout, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + subresourceRange + ); + sceneTextures.voxels.albedo.generateMipmaps( commandBuffer, 0 ); + sceneTextures.voxels.albedo.setImageLayout( + commandBuffer, + sceneTextures.voxels.albedo.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + sceneTextures.voxels.albedo.imageLayout, + subresourceRange + ); + }); + } +#endif +} +void ext::VoxelizerBehavior::tick( uf::Object& self ) { +#if UF_USE_VULKAN + if ( !this->hasComponent() ) return; + + auto& metadata = this->getComponent(); + auto& renderMode = this->getComponent(); + renderMode.setTarget(""); + if ( renderMode.executed && !metadata.initialized ) { + // renderMode.execute = false; + metadata.initialized = true; + } +#if COMP_SHADER_USED + auto& scene = uf::scene::getCurrentScene(); + ext::ExtSceneBehavior::bindBuffers( scene, metadata.renderModeName, true ); +#endif +#endif +} +void ext::VoxelizerBehavior::render( uf::Object& self ){} +void ext::VoxelizerBehavior::destroy( uf::Object& self ){ +#if UF_USE_VULKAN + if ( this->hasComponent() ) { + auto& renderMode = this->getComponent(); + uf::renderer::removeRenderMode( &renderMode, false ); + this->deleteComponent(); + } +#endif +} +#undef this \ No newline at end of file diff --git a/ext/behaviors/voxelizer/behavior.h b/ext/behaviors/voxelizer/behavior.h new file mode 100644 index 00000000..169068ac --- /dev/null +++ b/ext/behaviors/voxelizer/behavior.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace ext { + namespace VoxelizerBehavior { + UF_BEHAVIOR_DEFINE_TYPE; + void attach( uf::Object& ); + void initialize( uf::Object& ); + void tick( uf::Object& ); + void render( uf::Object& ); + void destroy( uf::Object& ); + struct Metadata { + pod::Vector3ui fragmentSize = { 0, 0 }; + pod::Vector3ui voxelSize = { 256, 256, 256 }; + pod::Vector3ui dispatchSize = { 8, 8, 8 }; + std::string renderModeName = "SVOGI"; + struct { + pod::Vector3f min = {}; + pod::Vector3f max = {}; + pod::Matrix4f matrix = uf::matrix::identity(); + } extents; + bool initialized = false; + }; + } +} \ No newline at end of file