SmokeLighting(SixWayLightmap)
環境
Unity2022.2.2f1
概要
UnityがHDRPでVfxGraphに実装した「Six Way Smoke Lit」的なもののテストです
ポイントライトはRenderModeを「Important」に設定しています
QuadのGameObjectを作成して、マテリアルを設定すれば確認できます
テクスチャは参考のUnityのブログ記事から使えるもののリンクがあります
コード
Shader "lit/SmokeSixWayLightmap" { Properties { _PisitiveTex("PisitiveTex", 2D) = "white" {} _NegativeTex("NegativeTex", 2D) = "white" {} _TexDivideX("TexDivideX", int) = 1 _TexDivideY("TexDivideY", int) = 1 [HDR]_EmissiveColor("EmissiveColor", Color) = (0,1,0,1) } CGINCLUDE #pragma enable_d3d11_debug_symbols #include "UnityCG.cginc" #include "Lighting.cginc" #define BILLBOARD struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD; #if !defined(BILLBOARD) float3 normal : NORMAL; float4 tangent : TANGENT; #endif }; struct v2f { float4 pos : SV_POSITION; float2 texcoord : TEXCOORD0; float3 lightDirTS : TEXCOORD1; float3 lightDirTS2 : TEXCOORD2; float3 lightColor : TEXCOORD3; float3 lightColor2 : TEXCOORD4; }; fixed4 _EmissiveColor; sampler2D _PisitiveTex; sampler2D _NegativeTex; uint _TexDivideX; uint _TexDivideY; float ComputeLightMap(float3 lightDirTS, float4 colPositiveRTBA, float4 colNegativeLBFE) { float lr = (lightDirTS.x > 0.0) ? (colPositiveRTBA.x) : (colNegativeLBFE.x); float tb = (lightDirTS.y > 0.0) ? (colPositiveRTBA.y) : (colNegativeLBFE.y); float fb = (-lightDirTS.z > 0.0) ? (colPositiveRTBA.z) : (colNegativeLBFE.z); float lightMap = lr * lightDirTS.x * lightDirTS.x + tb * lightDirTS.y * lightDirTS.y + fb * lightDirTS.z * lightDirTS.z; return lightMap; } // Shade4PointLightsから法線の処理を消したもの float3 FourPointLightsColor( float4 lightPosX, float4 lightPosY, float4 lightPosZ, float3 lightColor0, float3 lightColor1, float3 lightColor2, float3 lightColor3, float4 lightAttenSq, float3 pos) { // to light vectors float4 toLightX = lightPosX - pos.x; float4 toLightY = lightPosY - pos.y; float4 toLightZ = lightPosZ - pos.z; // squared lengths float4 lengthSq = 0; lengthSq += toLightX * toLightX; lengthSq += toLightY * toLightY; lengthSq += toLightZ * toLightZ; // don't produce NaNs if some vertex position overlaps with the light lengthSq = max(lengthSq, 0.000001); // attenuation float4 atten = 1.0 / (1.0 + lengthSq * lightAttenSq); float4 diff = atten; // final color float3 col = 0; col += lightColor0 * diff.x; col += lightColor1 * diff.y; col += lightColor2 * diff.z; col += lightColor3 * diff.w; return col; } ENDCG SubShader { Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" "LightMode" = "ForwardBase" } Pass { ZWrite Off Cull Back Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap #pragma vertex vert #pragma fragment frag v2f vert(appdata v) { v2f o; #if defined(BILLBOARD) // Billboard float3x3 mtxRot = unity_ObjectToWorld; mtxRot._m00_m10_m20 = normalize(mtxRot._m00_m10_m20); mtxRot._m01_m11_m21 = normalize(mtxRot._m01_m11_m21); mtxRot._m02_m12_m22 = normalize(mtxRot._m02_m12_m22); v.vertex.xyz = mul(v.vertex.xyz, mul(UNITY_MATRIX_V, mtxRot)); #endif // TextureAnimation const uint dx = _TexDivideX; const uint dy = _TexDivideY; const float tdx = 1.0 / dx; const float tdy = 1.0 / dy; uint index = (uint)fmod(_Time.y * 30.0, dx * dy); uint ix = index % dx; uint iy = (dy - 1) - (index / dx); o.texcoord = float2(v.texcoord.x, v.texcoord.y) * float2(tdx, tdy); o.texcoord.x += ix * tdx; o.texcoord.y += iy * tdy; o.pos = UnityObjectToClipPos(v.vertex); float3 posWS = mul(unity_ObjectToWorld, v.vertex).xyz; #if defined(BILLBOARD) float3 normalWS = -unity_CameraToWorld._m02_m12_m22; float3 tangentWS = unity_CameraToWorld._m00_m10_m20; float3 bitangentWS = unity_CameraToWorld._m01_m11_m21; #else fixed3 normalWS = UnityObjectToWorldNormal(v.normal); fixed3 tangentWS = UnityObjectToWorldDir(v.tangent.xyz); fixed3 bitangentWS = cross(normalWS, tangentWS) * v.tangent.w; #endif float3x3 mtxWSToTS = float3x3(tangentWS, bitangentWS, normalWS); // DirectionalLight float3 lightDirWS = _WorldSpaceLightPos0.xyz; o.lightDirTS = mul(mtxWSToTS, lightDirWS); o.lightColor = _LightColor0.rgb; // PointLights float3 lightPos = float3(0,0,0); uint lightCount = 0; [unroll] for (uint i = 0; i < 4; i++) { lightPos += float3(unity_4LightPosX0[i], unity_4LightPosY0[i], unity_4LightPosZ0[i]); lightCount += (unity_LightColor[i].a > 0) ? 1 : 0; } lightPos /= lightCount; float3 lightDirWS2 = normalize(lightPos - posWS.xyz); o.lightDirTS2 = mul(mtxWSToTS, lightDirWS2); o.lightColor2 = FourPointLightsColor( unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb, unity_4LightAtten0, posWS); return o; } half4 frag(v2f In) : COLOR { half4 colPositive = tex2D(_PisitiveTex, In.texcoord); half4 colNegative = tex2D(_NegativeTex, In.texcoord); // DirectionalLight float3 lightDirTS = normalize(In.lightDirTS); float lightmap01 = ComputeLightMap(lightDirTS, colPositive, colNegative); // PointLights float3 lightDirTS2 = normalize(In.lightDirTS2); float lightmap02 = ComputeLightMap(lightDirTS2, colPositive, colNegative); half3 rgb01 = lightmap01 * In.lightColor; half3 rgb02 = lightmap02 * In.lightColor2; half3 rgb = rgb01 + rgb02; half4 col = half4(rgb, colPositive.a); col.rgb += _EmissiveColor.rgb * colNegative.a; return col; } ENDCG } } }
参考
Visual Effect Graph の 6 ウェイライティングを使ったリアルな煙のライティング | Unity Blog
GitHub - keijiro/SixWaySmokeTest
6-way lightmap WIP - #20 by Aaron - Real Time VFX
この記事はFrontとBackに近似値を使用してチャンネルを節約したものを解説している?
Smoke Lighting and texture re-usability in Skull & Bones - Real Time VFX