// Marmoset Skyshop // Copyright 2013 Marmoset LLC // http://marmoset.co #ifndef MARMOSET_CORE_CGINC #define MARMOSET_CORE_CGINC #define INV_2PI 0.15915494309189533576888376337251 #define INV_2PIx2 0.31830988618379067153776752674502 //Linear - unity_ColorSpaceGrey.r is 0.19 //Gamma - unity_ColorSpaceGrey.r is 0.5 //linear ? 1 : 0 = (Grey - 0.5) / (0.19 - 0.5) = one clever MAD instruction #define IS_LINEAR ((-3.22581*unity_ColorSpaceGrey.r) + 1.6129) #define IS_GAMMA (( 3.22581*unity_ColorSpaceGrey.r) - 0.6129) #ifdef MARMO_SPECULAR_IBL uniform samplerCUBE _SpecCubeIBL; #endif uniform half4 _ExposureIBL; //IBL intensities uniform half2 _ExposureLM; //IBL intensities when lightmapping uniform half2 _UniformOcclusion; //Uniform diffuse and specular IBL occlusion terms uniform float4x4 _SkyMatrix; uniform float4x4 _InvSkyMatrix; uniform float3 _SkySize; uniform float3 _SkyMin; uniform float3 _SkyMax; uniform float3 _SH0; uniform float3 _SH1; uniform float3 _SH2; uniform float3 _SH3; uniform float3 _SH4; uniform float3 _SH5; uniform float3 _SH6; uniform float3 _SH7; uniform float3 _SH8; #ifdef MARMO_SKY_BLEND uniform float _BlendWeightIBL; uniform half4 _ExposureIBL1; //IBL intensities uniform half2 _ExposureLM1; //IBL intensities when lightmapping uniform float4x4 _SkyMatrix1; uniform float4x4 _InvSkyMatrix1; uniform float3 _SkySize1; uniform float3 _SkyMin1; uniform float3 _SkyMax1; #ifdef MARMO_SPECULAR_IBL uniform samplerCUBE _SpecCubeIBL1; #endif uniform float3 _SH01; uniform float3 _SH11; uniform float3 _SH21; uniform float3 _SH31; uniform float3 _SH41; uniform float3 _SH51; uniform float3 _SH61; uniform float3 _SH71; uniform float3 _SH81; #endif //Color-correction half3 toLinearApprox3(half3 c){ return c*c; } half3 toLinear3(half3 c) { return pow(c,2.2); } half toLinearFast1(half c) { half c2 = c*c; return dot(half2(0.7532,0.2468),half2(c2,c*c2)); } half3 toLinearFast3(half3 c) { half3 c2 = c*c; return 0.7532*c2 + 0.2468*c*c2; } half toGammaApprox1(half c) { return sqrt(c); } half3 toGammaApprox3(half3 c) { return sqrt(c); } half toGamma1(half c) { return pow(c,0.454545); } half3 toGamma3(half3 c) { return pow(c,0.454545); } half toGammaFast1(half c) { c = 1.0 - c; half c2 = c*c; half3 c16 = half3(c2*c2,c2,c); //^4 c16.x *= c16.x; //^8 c16.x *= c16.x; //^16 c16 = half3(1.0,1.0,1.0)-c16; return dot(half3(0.326999,0.249006,0.423995),c16); } half3 toGammaFast3(half3 c) { half3 one = half3(1.0,1.0,1.0); c = one - c; half3 c2 = c*c; half3 c16 = c2*c2; //^4 c16 *= c16; //^8 c16 *= c16; //^16 return 0.326999*(one-c16) + 0.249006*(one-c2) + 0.423995*(one-c); } //(1-t)a + t*a*a; //a - t*a + t*a*a; //a( 1-t + t*a); //mul(a, mad(t,a,1-t)); //4 inst, 2 inst if t and 1-t are constants //lerp(a, a*a, t); //4 inst, 3 inst if constant t half toGammaAuto1(half a) { return a * ((a * IS_LINEAR) + IS_GAMMA); } half3 toGammaAuto3(half3 a) { return a * ((a * IS_LINEAR.xxx) + IS_GAMMA.xxx); } //(1-t)*sqrt(a) + t*a //(1-s)*a + s*sqrt(a) //a - s*a + s*sqrt(a) //a - s*a + s*a/sqrt(a) //a*(1-s+s*invsqrt(a)) //mul(a,mad(s,invsqrt(a),1-s)) //mul(a,mad(s,invsqrt(a),t)) //5 inst, 3 inst if constant t //lerp(a*invsqrt(a), a, t) //5 inst, 4 if constant t half toLinearAuto1(half a) { return a*(IS_GAMMA*rsqrt(a) + IS_LINEAR); } half3 toLinearAuto3(half3 a) { return a*(IS_GAMMA.xxx*rsqrt(a) + IS_LINEAR.xxx); } //approximations for the true, step-wise gamma curve used in sRGB compression float linearTosRGB1( float c ) { float sqrtc = sqrt( c ); //SQRT/MAD/MAD/MAD (accurate) return (sqrtc - sqrtc*c) + c*(0.4672*c + 0.5328); } float3 linearTosRGB3( float3 c ) { float3 sqrtc = sqrt( c ); //SQRT/MAD/MAD/MAD (accurate) return (sqrtc - sqrtc*c) + c*(float3(0.4672,0.4672,0.4672)*c + float3(0.5328,0.5328,0.5328)); } float4 linearTosRGB4( float4 c ) { float4 sqrtc = sqrt( c ); //SQRT/MAD/MAD/MAD (accurate) return (sqrtc - sqrtc*c) + c*(float4(0.4672,0.4672,0.4672,0.4672)*c + float4(0.5328,0.5328,0.5328,0.5328)); } float sRGBToLinear1( float c ) { return (c*c)*(c*0.2848 + 0.7152); //MAD/MUL/MUL (accurate) } float3 sRGBToLinear3( float3 c ) { return (c*c)*(c*float3(0.2848,0.2848,0.2848) + float3(0.7152,0.7152,0.7152)); //MAD/MUL/MUL (accurate) } float4 sRGBToLinear4( float4 c ) { return (c*c)*(c*float4(0.2848,0.2848,0.2848,0.2848) + float4(0.7152,0.7152,0.7152,0.7152)); //MAD/MUL/MUL (accurate) } float3 mulVec3( float4x4 m, float3 v ) { return float3(dot(m[0].xyz,v.xyz), dot(m[1].xyz,v.xyz), dot(m[2].xyz,v.xyz)); } float3 mulPoint3( float4x4 m, float3 p ) { float4 v = float4(p.x,p.y,p.z,1.0); return float3(dot(m[0],v), dot(m[1],v), dot(m[2],v)); } float3 transposeMulVec3( float4x4 m, float3 v ) { return m[0].xyz*v.x + (m[1].xyz*v.y + (m[2].xyz*v.z)); } float3 transposeMulVec3( float3x3 m, float3 v ) { return m[0].xyz*v.x + (m[1].xyz*v.y + (m[2].xyz*v.z)); } float3 transposePoint3( float4x4 m, float3 p ) { return m[0].xyz*p.x + (m[1].xyz*p.y + (m[2].xyz*p.z + m[3].xyz)); } half3 fromRGBM(half4 c) { c.a *= 6.0; return c.rgb * lerp(c.a, toLinearFast1(c.a), IS_LINEAR); } half3 diffCubeLookup(samplerCUBE diffCube, float3 worldNormal) { half4 diff = texCUBE(diffCube, worldNormal); return fromRGBM(diff); } half3 specCubeLookup(samplerCUBE specCube, float3 worldRefl) { half4 spec = texCUBE(specCube, worldRefl); return fromRGBM(spec); } half3 glossCubeLookup(samplerCUBE specCube, float3 worldRefl, float glossLod) { #ifdef MARMO_BIAS_GLOSS half4 lookup = half4(worldRefl,glossLod); half4 spec = texCUBEbias(specCube, lookup); #else half4 lookup = half4(worldRefl,glossLod); half4 spec = texCUBElod(specCube, lookup); #endif return fromRGBM(spec); } half glossLOD(half glossMap, half shininess) { glossMap = 1.0-glossMap; glossMap = 1.0-(glossMap*glossMap); return 7.0 + glossMap - (shininess*glossMap); } half glossExponent(half glossLod) { return exp2(8.0-glossLod); } //returns 1/spec. function integral float specEnergyScalar(float gloss) { return (gloss*INV_2PI) + INV_2PIx2; } half splineFresnel(float3 N, float3 E, half specIntensity, half fresnel) { half factor = 1.0-saturate(dot(N,E)); half factor3 = factor*factor*factor; //a spline between 1, factor, and factor^3 half3 p = half3(1.0, factor, factor3); half2 t = half2(1.0-fresnel,fresnel); p.x = dot(p.xy,t); p.y = dot(p.yz,t); factor = 0.05 + 0.95 * dot(p.xy,t); factor *= specIntensity; //The math above is performed in linear space. If rendering in gamma space, //fresnel*specInt needs to be applied in gamma-space. // Note: specInt is also a linear value, it comes from a slider. factor = lerp(sqrt(factor), factor, IS_LINEAR); return factor; } half fastFresnel(float3 N, float3 E, half specIntensity, half fresnel) { //fresnel math performed in gamma space half factor = 1.0-saturate(dot(N,E)); factor *= 0.5 + 0.5*factor; factor = 0.15 + factor*0.85; factor = lerp(1.0, factor, fresnel); factor = specIntensity * factor; factor = lerp(factor, factor*factor, IS_LINEAR); return factor; } float3 skyRotate(uniform float4x4 skyMatrix, float3 R) { #ifdef MARMO_SKY_ROTATION R = transposeMulVec3(skyMatrix,R); #endif return R; } float3 skyProject(uniform float4x4 skyMatrix, uniform float4x4 invSkyMatrix, uniform float3 skyMin, uniform float3 skyMax, float3 worldPos, float3 R) { #ifdef MARMO_BOX_PROJECTION //box projection happens in sky-space #ifdef MARMO_SKY_ROTATION R = transposeMulVec3(skyMatrix,R).xyz; //HACK: mulVec3 is mul(transpose(_sky)) #endif float3 invR = 1.0/R; #ifdef MARMO_SKY_ROTATION float3 P = mulPoint3(invSkyMatrix,worldPos); #else float3 P = worldPos - skyMatrix[3].xyz; #endif float3 rbminmax = lerp(skyMin, skyMax, saturate(R*1000000.0)); rbminmax = (rbminmax - P.xyz) * invR; float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z); //R in projected sky space return (R*fa) + P.xyz; #else #ifdef MARMO_SKY_ROTATION R = transposeMulVec3(skyMatrix,R); #endif return R; #endif } //converts linear, HDR color to RGBM encoded data, ready for screen output float4 HDRtoRGBM(float4 color) { float toLinear = 2.2; float toGamma = 1.0/2.2; color.rgb = pow(color.rgb, toGamma); //RGBM gamma compression is 1/2.2 color *= 1.0/6.0; float m = max(max(color.r,color.g),color.b); m = saturate(m); m = ceil(m*255.0)/255.0; if( m > 0.0 ) { float inv_m = 1.0/m; color.rgb = saturate(color.rgb*inv_m); color.a = m; } else { color = half4(0.0,0.0,0.0,0.0); } return color; } float3 SHLookup(float3 dir) { //l = 0 band (constant) float3 result = _SH0.xyz; //l = 1 band result += _SH1.xyz * dir.y; result += _SH2.xyz * dir.z; result += _SH3.xyz * dir.x; //l = 2 band float3 swz = dir.yyz * dir.xzx; result += _SH4.xyz * swz.x; result += _SH5.xyz * swz.y; result += _SH7.xyz * swz.z; float3 sqr = dir * dir; result += _SH6.xyz * ( 3.0*sqr.z - 1.0 ); result += _SH8.xyz * ( sqr.x - sqr.y ); return abs(result); } void SHLookup(float3 dir, out float3 band0, out float3 band1, out float3 band2) { //l = 0 band (constant) band0 = _SH0.xyz; //l = 1 band band1 = _SH1.xyz * dir.y; band1 += _SH2.xyz * dir.z; band1 += _SH3.xyz * dir.x; //l = 2 band float3 swz = dir.yyz * dir.xzx; band2 = _SH4.xyz * swz.x; band2 += _SH5.xyz * swz.y; band2 += _SH7.xyz * swz.z; float3 sqr = dir * dir; band2 += _SH6.xyz * ( 3.0*sqr.z - 1.0 ); band2 += _SH8.xyz * ( sqr.x - sqr.y ); } #ifdef MARMO_SKY_BLEND float3 SHLookup1(float3 dir) { //l = 0 band (constant) float3 result = _SH01.xyz; //l = 1 band result += _SH11.xyz * dir.y; result += _SH21.xyz * dir.z; result += _SH31.xyz * dir.x; //l = 2 band float3 swz = dir.yyz * dir.xzx; result += _SH41.xyz * swz.x; result += _SH51.xyz * swz.y; result += _SH71.xyz * swz.z; float3 sqr = dir * dir; result += _SH61.xyz * ( 3.0*sqr.z - 1.0 ); result += _SH81.xyz * ( sqr.x - sqr.y ); return abs(result); } void SHLookup1(float3 dir, out float3 band0, out float3 band1, out float3 band2) { //l = 0 band (constant) band0 = _SH01.xyz; //l = 1 band band1 = _SH11.xyz * dir.y; band1 += _SH21.xyz * dir.z; band1 += _SH31.xyz * dir.x; //l = 2 band float3 swz = dir.yyz * dir.xzx; band2 = _SH41.xyz * swz.x; band2 += _SH51.xyz * swz.y; band2 += _SH71.xyz * swz.z; float3 sqr = dir * dir; band2 += _SH61.xyz * ( 3.0*sqr.z - 1.0 ); band2 += _SH81.xyz * ( sqr.x - sqr.y ); } #endif float3 SHLookupUnity(float3 dir) { return ShadeSH9(half4(dir.x, dir.y, dir.z,1.0)); } void SHLookupUnity(float3 dir, out float3 band0, out float3 band1, out float3 band2) { //constant term band0 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w); // Linear term band1.r = dot(unity_SHAr.xyz, dir.xyz); band1.g = dot(unity_SHAg.xyz, dir.xyz); band1.b = dot(unity_SHAb.xyz, dir.xyz); // 4 of the quadratic polynomials half4 vB = dir.xyzz * dir.yzzx; band2.r = dot(unity_SHBr,vB); band2.g = dot(unity_SHBg,vB); band2.b = dot(unity_SHBb,vB); // Final quadratic polynomial float vC = dir.x*dir.x - dir.y*dir.y; band2 += unity_SHC.rgb * vC; } float3 SHConvolve(float3 band0, float3 band1, float3 band2, float3 weight) { float3 conv1 = lerp( float3(1.0,1.0,1.0), float3(0.6667,0.6667,0.6667), weight); float3 conv2 = lerp( float3(1.0,1.0,1.0), float3(0.25,0.25,0.25), weight); conv1 = lerp(conv1, conv1*conv1, weight); conv2 = lerp(conv2, conv2*conv2, weight); return abs(band0 + band1*conv1 + band2*conv2); } #endif