diff --git a/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl index af6a61ca0..409ad4e0f 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl @@ -7,37 +7,38 @@ struct ConvexConnectP { float4 pos : TORQUE_POSITION; - float4 wsEyeDir : TEXCOORD0; - float4 ssPos : TEXCOORD1; + float4 uv0 : TEXCOORD1; float4 vsEyeDir : TEXCOORD2; }; TORQUE_UNIFORM_SAMPLER2D(deferredBuffer, 0); TORQUE_UNIFORM_SAMPLER2D(colorBuffer, 1); TORQUE_UNIFORM_SAMPLER2D(matInfoBuffer, 2); -TORQUE_UNIFORM_SAMPLERCUBE(cubeMap, 3); -TORQUE_UNIFORM_SAMPLERCUBE(irradianceCubemap, 4); TORQUE_UNIFORM_SAMPLER2D(BRDFTexture, 5); -uniform float cubeMips; uniform float4 rtParams0; - -uniform float3 probeWSPos; -uniform float3 probeLSPos; uniform float4 vsFarPlane; - -uniform float radius; -uniform float2 attenuation; - -uniform float4x4 worldToObj; uniform float4x4 cameraToWorld; - uniform float3 eyePosWorld; -uniform float3 bbMin; -uniform float3 bbMax; -uniform float useSphereMode; +//cubemap arrays require all the same size. so shared mips# value +uniform float cubeMips; +#define MAX_PROBES 50 +uniform float numProbes; +TORQUE_UNIFORM_SAMPLERCUBEARRAY(cubeMap[MAX_PROBES], 3); +TORQUE_UNIFORM_SAMPLERCUBEARRAY(irradianceCubemap[MAX_PROBES] 4); +uniform float3 inProbePosArray[MAX_PROBES]; +uniform float4x4 worldToObjArray[MAX_PROBES]; +uniform float3 bbMinArray[MAX_PROBES]; +uniform float3 bbMaxArray[MAX_PROBES]; +uniform float useSphereMode[MAX_PROBES]; +uniform float radius[MAX_PROBES]; +uniform float2 attenuation[MAX_PROBES]; + +// Box Projected IBL Lighting +// Based on: http://www.gamedev.net/topic/568829-box-projected-cubemap-environment-mapping/ +// and https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ float3 boxProject(float3 wsPosition, float3 reflectDir, float3 boxWSPos, float3 boxMin, float3 boxMax) { float3 nrdir = reflectDir; @@ -52,12 +53,23 @@ float3 boxProject(float3 wsPosition, float3 reflectDir, float3 boxWSPos, float3 return posonbox - boxWSPos; } +float3 iblBoxDiffuse( Surface surface, + TORQUE_SAMPLERCUBE(irradianceCube), + float3 boxPos, + float3 boxMin, + float3 boxMax) +{ + float3 cubeN = boxProject(surface.P, surface.N, boxPos, boxMin, boxMax); + cubeN.z *=-1; + return TORQUE_TEXCUBELOD(irradianceCube, float4(cubeN,0)).xyz; +} + float3 iblBoxSpecular(float3 normal, float3 wsPos, float roughness, float3 surfToEye, TORQUE_SAMPLER2D(brdfTexture), - //TORQUE_SAMPLERCUBE(radianceCube), - float3 boxPos, - float3 boxMin, - float3 boxMax) + TORQUE_SAMPLERCUBE(radianceCube), + float3 boxPos, + float3 boxMin, + float3 boxMax) { float ndotv = clamp(dot(normal, surfToEye), 0.0, 1.0); @@ -65,88 +77,115 @@ float3 iblBoxSpecular(float3 normal, float3 wsPos, float roughness, float3 surfT float2 brdf = TORQUE_TEX2DLOD(brdfTexture, float4(roughness, ndotv,0.0,0.0)).xy; // Radiance (Specular) - float maxmip = pow(cubeMips+1,2); - float lod = roughness*maxmip; + float lod = roughness*cubeMips; float3 r = reflect(surfToEye, normal); float3 cubeR = normalize(r); cubeR = boxProject(wsPos, cubeR, boxPos, boxMin, boxMax); - float3 radiance = float3(1,1,1);//TORQUE_TEXCUBELOD(radianceCube, float4(cubeR, lod)).xyz * (brdf.x + brdf.y); + float3 radiance = TORQUE_TEXCUBELOD(radianceCube, float4(cubeR, lod)).xyz * (brdf.x + brdf.y); return radiance; } -float4 main(ConvexConnectP IN) : SV_TARGET +float defineBoxSpaceInfluence(float3 surfPosWS, float3 probePos, float4x4 worldToObj, float radius, float atten) { - // Compute scene UV - float3 ssPos = IN.ssPos.xyz / IN.ssPos.w; - float2 uvScene = getUVFromSSPos( ssPos, rtParams0 ); + float3 surfPosLS = mul( worldToObj, float4(surfPosWS,1.0)).xyz; + float3 boxMinLS = probePos-(float3(1,1,1)*radius); + float3 boxMaxLS = probePos+(float3(1,1,1)*radius); + + float boxOuterRange = length(boxMaxLS - boxMinLS); + float boxInnerRange = boxOuterRange / atten; + + float3 localDir = float3(abs(surfPosLS.x), abs(surfPosLS.y), abs(surfPosLS.z)); + localDir = (localDir - boxInnerRange) / (boxOuterRange - boxInnerRange); + + return max(localDir.x, max(localDir.y, localDir.z)) * -1; +} + +float4 main( ConvexConnectP IN ) : SV_TARGET +{ //eye ray WS/LS float3 vsEyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN.vsEyeDir.xyz, vsFarPlane ); float3 wsEyeRay = mul(cameraToWorld, float4(vsEyeRay, 0)).xyz; //unpack normal and linear depth - float4 normDepth = TORQUE_DEFERRED_UNCONDITION(deferredBuffer, uvScene); + float4 normDepth = TORQUE_DEFERRED_UNCONDITION(deferredBuffer, IN.uv0); //create surface Surface surface = createSurface( normDepth, TORQUE_SAMPLER2D_MAKEARG(colorBuffer),TORQUE_SAMPLER2D_MAKEARG(matInfoBuffer), - uvScene, eyePosWorld, wsEyeRay, cameraToWorld); - float blendVal = 1.0; + IN.uv0, eyePosWorld, wsEyeRay, cameraToWorld); + //early out if emissive + if (getFlag(surface.matFlag, 0)) + { + discard; + } + float blendVal[MAX_PROBES]; + float3 surfToEye = normalize(surface.P - eyePosWorld); - [unroll] - for(float i=0; i < 10; i++) - { - float3 probeWSPos = float3(i * 1.1, 0, 2); - float3 L = probeWSPos - surface.P; - blendVal = 1.0-length(L)/radius; - clip(blendVal); - } + int i; + float3 irradiance = float3(0,0,0); + float3 specular = float3(0,0,0); + float blendSum = 0; + float invBlendSum = 0; + + for(i=0; i < numProbes; i++) + { + float3 probeWS = inProbePosArray[i]; + float3 L = probeWS - surface.P; + + if(useSphereMode[i]) + { + float3 L = inProbePosArray[i] - surface.P; + blendVal[i] = 1.0-length(L)/radius[i]; + blendVal[i] = max(0,blendVal[i]); + } + else + { + float tempAttenVal = 3.5; + blendVal[i] = defineBoxSpaceInfluence(surface.P, inProbePosArray[i], worldToObjArray[i], radius[i], tempAttenVal); + blendVal[i] = max(0,blendVal[i]); + } + blendSum += blendVal[i]; + invBlendSum +=(1.0f - blendVal[i]); + } + + // Weight0 = normalized NDF, inverted to have 1 at center, 0 at boundary. + // And as we invert, we need to divide by Num-1 to stay normalized (else sum is > 1). + // respect constraint B. + // Weight1 = normalized inverted NDF, so we have 1 at center, 0 at boundary + // and respect constraint A. + for(i=0; i < numProbes; i++) + { + blendVal[i] = (1.0f - ( blendVal[i] / blendSum)) / (numProbes - 1); + blendVal[i] *= ((1.0f - blendVal[i]) / invBlendSum); + blendSum += blendVal[i]; + } - //render into the bound space defined above - float3 surfToEye = normalize(surface.P - eyePosWorld); - float3 irradiance = float3(1,0,0);//TORQUE_TEXCUBELOD(irradianceCubemap, float4(surface.N,0)).xyz; - float3 specular = iblBoxSpecular(surface.N, surface.P, surface.roughness, surfToEye, TORQUE_SAMPLER2D_MAKEARG(BRDFTexture)/*, TORQUE_SAMPLERCUBE_MAKEARG(cubeMap)*/, probeWSPos, bbMin, bbMax); + // Normalize blendVal + if (blendSum == 0.0f) // Possible with custom weight + { + blendSum = 1.0f; + } + + float invBlendSumWeighted = 1.0f / blendSum; + for (int i = 0; i < numProbes; ++i) + { + blendVal[i] *= invBlendSumWeighted; + } + + float3 irradiance = float3(0,0,0); + float3 specular = float3(0,0,0); float3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); - specular *= F; //energy conservation - float3 kD = 1.0.xxx - F; - kD *= 1.0 - surface.metalness; + float3 kD = 1.0.xxx - F; + kD *= 1.0 - surface.metalness; + for (int i = 0; i < numProbes; ++i) + { + irradiance += blendVal[i]*iblBoxDiffuse(surface,TORQUE_SAMPLERCUBE_MAKEARG(irradianceCubemap[i]), inProbePosArray[i], bbMinArray[i], bbMaxArray[i]); + specular += blendVal[i]*F*iblBoxSpecular(surface.N, surface.P, surface.roughness, surfToEye, TORQUE_SAMPLER2D_MAKEARG(BRDFTexture), TORQUE_SAMPLERCUBE_MAKEARG(cubeMap[i]), inProbePosArray[i], bbMinArray[i], bbMaxArray[i]); + } //final diffuse color float3 diffuse = kD * irradiance * surface.baseColor.rgb; - - return float4(diffuse + specular * surface.ao, blendVal); - - /*float3 N = getCubeDir(face,IN.uv); - float3 irradiance = 0; - - // tangent space calculation from origin point - float3 up = float3(0.0, 0.0, 1.0); - float3 right = cross(up, N); - up = cross(N, right); - - float sampleDelta = 0.025; - int nrSamples = 0; - for(float phi = 0.0; phi < M_2PI_F; phi += sampleDelta) - { - for(float theta = 0.0; theta < M_HALFPI_F; theta += sampleDelta) - { - // spherical to cartesian (in tangent space) - float3 tangentSample = float3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); - // tangent space to world - float3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; - - irradiance += TORQUE_TEXCUBE(environmentMap, sampleVec).rgb * cos(theta) * sin(theta); - nrSamples++; - } - } - irradiance = M_PI_F * irradiance * (1.0 / float(nrSamples)); - - return float4(irradiance, 1.0);*/ - - //return float4(1,0,0,1); - - //float4 texCoord = float4( 1,0,0, 1 ); - - //return texCoord; -} \ No newline at end of file + return float4(diffuse + specular * surface.ao, blendVal); +} diff --git a/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayV.hlsl b/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayV.hlsl new file mode 100644 index 000000000..9fa2eda0e --- /dev/null +++ b/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayV.hlsl @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "../../hlslStructs.hlsl" +#include "../../shaderModel.hlsl" +#include "../../torque.hlsl" + +struct GFXVertexPT +{ + float3 pos : POSITION; + float4 uv : TEXCOORD1; +}; + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float4 uv0 : TEXCOORD1; + float4 vsEyeDir : TEXCOORD2; +}; + +uniform float4 rtParams0; +uniform float4x4 worldViewOnly; + +ConnectData main( GFXVertexPT IN ) +{ + ConnectData OUT; + + OUT.hpos = float4(IN.pos,1.0); + OUT.uv0 = float4(viewportCoordToRenderTarget( IN.uv, rtParams0 ),0,0); + OUT.vsEyeDir = mul(worldViewOnly, float4(IN.pos, 1.0)); + + return OUT; +} diff --git a/Templates/Full/game/shaders/common/shaderModel.hlsl b/Templates/Full/game/shaders/common/shaderModel.hlsl index 986caadb6..4234ef2d8 100644 --- a/Templates/Full/game/shaders/common/shaderModel.hlsl +++ b/Templates/Full/game/shaders/common/shaderModel.hlsl @@ -40,12 +40,14 @@ #define TORQUE_UNIFORM_SAMPLER2DCMP(tex,regist) uniform Texture2D texture_##tex : register(T##regist); uniform SamplerComparisonState tex : register(S##regist) #define TORQUE_UNIFORM_SAMPLER3D(tex,regist) uniform Texture3D texture_##tex : register(T##regist); uniform SamplerState tex : register(S##regist) #define TORQUE_UNIFORM_SAMPLERCUBE(tex,regist) uniform TextureCube texture_##tex : register(T##regist); uniform SamplerState tex : register(S##regist) +#define TORQUE_UNIFORM_SAMPLERCUBEARRAY(tex,regist) uniform TextureCubeArray texture_##tex : register(T##regist); uniform SamplerState tex : register(S##regist) // Sampling functions #define TORQUE_TEX1D(tex,coords) texture_##tex.Sample(tex,coords) #define TORQUE_TEX2D(tex,coords) texture_##tex.Sample(tex,coords) #define TORQUE_TEX2DPROJ(tex,coords) texture_##tex.Sample(tex,coords.xy / coords.w) #define TORQUE_TEX3D(tex,coords) texture_##tex.Sample(tex,coords) #define TORQUE_TEXCUBE(tex,coords) texture_##tex.Sample(tex,coords) +#define TORQUE_TEXCUBEARRAY(tex,coords) texture_##tex.Sample(tex,coords) // The mipmap LOD is specified in coord.w #define TORQUE_TEX2DLOD(tex,coords) texture_##tex.SampleLevel(tex,coords.xy,coords.w) #define TORQUE_TEXCUBELOD(tex,coords) texture_##tex.SampleLevel(tex,coords.xyz,coords.w)