Unity renders fog based on depth. This leads to the "fog-wall" rotating with the camera. Is there a cheap way(mobile) of using a radial distance as depth? How would i tell Unity to render fog differently anyhow, i am just starting shaders? It doesn't have to be perfectly radial, only approximated, i just want to reduce the effect of buildings popping in and out when rotating the camera: Maybe something that uses the pixel position (ComputeScreenPos) and factors it in, so i can increase the fog value at the edges of the screen. but i have no idea on how to go about this
Using distance instead of depth is fairly simple (and more correct.) It's fairly cheap too, so I don't know why this isn't the default, because it prevents this type of issue. As far as I know, there is no distance mode for the Unity fog, so you'll have to write it yourself. The common way to do this is to add it as a postprocess effect on the Camera. (But you can also add it directly to the shader itself in some cases.) Basic formula is: oneminusfog = exp ( -distance ( camera, pixel ) * fog_density ) result = lerp ( fog_color, result, oneminusfog )
Yes, I was in doubt to mention that. There are other (cheaper) blending ways you can still base on the same distance metric. But exponential is the physical way to do it.
Thanks for the answer guys, as of now i am struggling on where to put this stuff. I've got my copy of the diffuse legacy terrain shader. There is that line that says "#pragma multi_compile_fog" that contols fog. - i guess i must overwrite this line for each shader i want to affect with it. Something like #pragma multi_myNewFog - How and where do i define that "multi_myNewFog"
It's a bit more complex than than just replacing the #pragma, and you may not even want to do that depending on how you want to implement this. My solution was to replace the UnityCG.cginc file with one that implemented distance fog for everything (Unity uses depth fog). For a custom shader you'll want to pass the world or view position from the vertex shader and calculate the distance in the fragment shader and then lerp to a color based on that distance. Assuming you don't care about different fog types, you can remove the #pragma line all together. I would suggest using the unity_FogColor and unity_FogParams still just for simplicity, but you should look in UnityCG.cginc to see what Unity is doing for fog.
Keijiro's fade to skybox fog includes an option for radial fog, you might want to take a look at that https://github.com/keijiro/KinoFog
Wow, Thanks! This is exactly what i also needed, i just downloaded the package, lets see if this effect is mobile friendly... EDIT: Nope, the skybox blending is a postprocessing effect, so it wont do any good on mobile, but the radial fog seems to be just what i need, now i just need to extract it
Small followup: I don't know if i did something that caused it but it seems that vertexLit shaders use radial fog by default
I tinkered around with shader programming. You would have to create your own fog-shader or change the FOG-Code in the "UnityCG.cginc" file. I did not get far with it. I shifted this task back in my schedule because most of my shaders are vertex anyway. But this is the work in progress testing shader i got so far: Spoiler: A Mess of a Shader Code (CSharp): // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt) // Simplified VertexLit shader. Differences from regular VertexLit one: // - no per-material color // - no specular // - no emission // - default "Mobile/VertexLit" Shader "InfinityTerrain/ManualFog" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { Tags { "LightMode" = "Vertex" } //Tags { "LightMode" = "ForwardBase" } // Cull Back // Lighting On CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #pragma glsl//tex2Dlod #include "UnityCG.cginc" struct appdata { fixed4 vertex : POSITION; fixed2 uv : TEXCOORD0; fixed3 normal : NORMAL; fixed3 color : COLOR; }; struct v2f { fixed2 uv : TEXCOORD0; //UNITY_FOG_COORDS(1) fixed4 vertex : SV_POSITION; fixed3 color : COLOR; }; sampler2D _MainTex; fixed4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); //#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw) //?v.uv.xy *_MainTex##_ST.xy +MainTex##_ST.zw;// o.color = ShadeVertexLights(v.vertex,v.normal); //Texture //read tex in vert shader already, no need to do it in frag shader fixed4 tex = tex2Dlod (_MainTex, fixed4(v.uv.xy,0,0)); o.color = tex *o.color; //Distance // o.fogDepth.x = length(mul (UNITY_MATRIX_MV, v.vertex).xyz); // o.fogDepth.y = o.fogDepth.x * unity_FogDensity; // // fixed dist = length(mul (UNITY_MATRIX_MV, v.vertex).xyz); // fixed fac = dist * 0.0025; fixed dist = length(ObjSpaceViewDir(v.vertex)); fixed fac = clamp( (dist) * 0.0125, 0, 1); // o.color = fixed3(fac,fac,fac); o.color = lerp( o.color, unity_FogColor , fac ); // UNITY_LIGHTMODEL_AMBIENT //debug // o.color = lerp( o.color, fixed3(1,0,0) , fac ); //check edge popping // if(fac<1){fac = 0;} // o.color = lerp( fixed3(1,0,0), unity_FogColor , fac ); //UNITY_TRANSFER_FOG(o,o.vertex); return o; } // fixed4 fixed3 frag (v2f i) : SV_Target { return i.color; // fixed4 col; // col.rgb = i.color;//fog debug // col = tex2D(_MainTex, i.uv); // sample the texture // col.rgb = col.rgb * i.color;//add lighting https://forum.unity.com/threads/vertexlit-surface-or-cg-shader.209649/ //UNITY_APPLY_FOG(i.fogCoord, col); // apply fog // return col; } ENDCG } } } /* FROM "UnityCG.cginc" // ------------------------------------------------------------------ // Fog helpers // // multi_compile_fog Will compile fog variants. // UNITY_FOG_COORDS(texcoordindex) Declares the fog data interpolator. // UNITY_TRANSFER_FOG(outputStruct,clipspacePos) Outputs fog data from the vertex shader. // UNITY_APPLY_FOG(fogData,col) Applies fog to color "col". Automatically applies black fog when in forward-additive pass. // Can also use UNITY_APPLY_FOG_COLOR to supply your own fog color. // In case someone by accident tries to compile fog code in one of the g-buffer or shadow passes: // treat it as fog is off. #if defined(UNITY_PASS_PREPASSBASE) || defined(UNITY_PASS_DEFERRED) || defined(UNITY_PASS_SHADOWCASTER) #undef FOG_LINEAR #undef FOG_EXP #undef FOG_EXP2 #endif #if defined(UNITY_REVERSED_Z) //D3d with reversed Z => z clip range is [near, 0] -> remapping to [0, far] //max is required to protect ourselves from near plane not being correct/meaningfull in case of oblique matrices. #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) max(((1.0-(coord)/_ProjectionParams.y)*_ProjectionParams.z),0) #elif UNITY_UV_STARTS_AT_TOP //D3d without reversed z => z clip range is [0, far] -> nothing to do #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) (coord) #else //Opengl => z clip range is [-near, far] -> should remap in theory but dont do it in practice to save some perf (range is close enought) #define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) (coord) #endif #if defined(FOG_LINEAR) // factor = (end-z)/(end-start) = z * (-1/(end-start)) + (end/(end-start)) #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = (coord) * unity_FogParams.z + unity_FogParams.w #elif defined(FOG_EXP) // factor = exp(-density*z) #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.y * (coord); unityFogFactor = exp2(-unityFogFactor) #elif defined(FOG_EXP2) // factor = exp(-(density*z)^2) #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.x * (coord); unityFogFactor = exp2(-unityFogFactor*unityFogFactor) #else #define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = 0.0 #endif #define UNITY_CALC_FOG_FACTOR(coord) UNITY_CALC_FOG_FACTOR_RAW(UNITY_Z_0_FAR_FROM_CLIPSPACE(coord)) #define UNITY_FOG_COORDS_PACKED(idx, vectype) vectype fogCoord : TEXCOORD##idx; #if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2) #define UNITY_FOG_COORDS(idx) UNITY_FOG_COORDS_PACKED(idx, float1) #if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE) // mobile or SM2.0: calculate fog factor per-vertex #define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord.x = unityFogFactor #else // SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z #endif #else #define UNITY_FOG_COORDS(idx) #define UNITY_TRANSFER_FOG(o,outpos) #endif #define UNITY_FOG_LERP_COLOR(col,fogCol,fogFac) col.rgb = lerp((fogCol).rgb, (col).rgb, saturate(fogFac)) #if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2) #if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE) // mobile or SM2.0: fog factor was already calculated per-vertex, so just lerp the color #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol) UNITY_FOG_LERP_COLOR(col,fogCol,(coord).x) #else // SM3.0 and PC/console: calculate fog factor and lerp fog color #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol) UNITY_CALC_FOG_FACTOR((coord).x); UNITY_FOG_LERP_COLOR(col,fogCol,unityFogFactor) #endif #else #define UNITY_APPLY_FOG_COLOR(coord,col,fogCol) #endif #ifdef UNITY_PASS_FORWARDADD #define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,fixed4(0,0,0,0)) #else #define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,unity_FogColor) #endif */ /*{ //https://forum.unity.com/threads/solved-accessing-fog-parameters-in-cg-shader.249886/ Properties { _MainTex ("Base (RGB)", 2D) = "black" {} } SubShader { Tags { "RenderType" = "Opaque" } Cull off // Lighting Off Blend SrcAlpha OneMinusSrcAlpha Fog {Mode Off} Pass { CGPROGRAM #pragma target 2.0 #pragma vertex vert #pragma fragment frag //#pragma exclude_renderers d3d11 xbox360 ps3 flash d3d11_9x #include "UnityCG.cginc" //uniform fixed3 unity_FogColor; uniform half unity_FogDensity; sampler2D _MainTex; struct vertexInput { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; float3 normal : NORMAL; fixed4 color : COLOR; }; struct v2f { float4 scrPos : SV_POSITION; half2 srcUVs: TEXCOORD0; half2 fogDepth: TEXCOORD1; // linear depth in x and depth multiplied by density in y float3 color : COLOR; }; v2f vert (vertexInput v) { v2f o; o.scrPos = UnityObjectToClipPos(v.vertex); //o.srcUVs = TRANSFORM_TEX(v.texcoord, _MainTex); o.srcUVs = v.texcoord; o.color = ShadeVertexLights(v.vertex,v.normal); // this is UNITY_FOG_COORDS // o.fogDepth.x = length(mul (UNITY_MATRIX_MV, v.vertex).xyz); // o.fogDepth.y = o.fogDepth.x * unity_FogDensity; o.fogDepth.x = length(mul (UNITY_MATRIX_MV, v.vertex).xyz); o.fogDepth.y = o.fogDepth.x * unity_FogDensity; return o; } fixed4 frag (v2f i) : COLOR { fixed3 clr = tex2D(_MainTex, i.srcUVs).rgb; clr.rgb = clr.rgb * i.color; // Exp2 mode: //float fogAmt = i.fogDepth.y * i.fogDepth.y; // fogAmt = exp(-fogAmt); float fogAmt = i.fogDepth.y; clr = lerp( unity_FogColor, clr, fogAmt); return fixed4( clr, 1.0 ); } ENDCG } } }*/
How exactly should I modify UnityCG,cginc file to make the Linear fog use radial distance from the camera? Should I edit this part: Code (CSharp): #if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2) #define UNITY_FOG_COORDS(idx) UNITY_FOG_COORDS_PACKED(idx, float1) #if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE) // mobile or SM2.0: calculate fog factor per-vertex #define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord.x = unityFogFactor #else // SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z #endif #else #define UNITY_FOG_COORDS(idx) #define UNITY_TRANSFER_FOG(o,outpos) #endif and replace UNITY_CALC_FOG_FACTOR((outpos).z) with UNITY_CALC_FOG_FACTOR_RAW(length(i.eyeVec)) ? Thank you very much.
I don't know, i ended up using only vertex shaders in my project where radial fog is used by default. You could just try what you written above and report your success. Here is how you would include a custom cginc file (if you don't want to modify your default one) https://forum.unity.com/threads/include-custom-cginc-files.94518/
Thank you. But unfortunately this gives me an error: "Shader error in 'Standard': undeclared identifier 'i' at /Programs/Unity5.6.4/Editor/Data/CGIncludes/UnityStandardCore.cginc(401) (on d3d11)" In the part of code starting after this line: // SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel I tried to replace #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z with #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = length(i.eyeVec) or with #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = UNITY_CALC_FOG_FACTOR_RAW(length(i.eyeVec))
Vertex shader calculate stuff per vertex,If your poly count is high,Youll get a sphere fog,If its low,Youll get bugged one
Any update on this? Trying to create a custom fog solution that has radial falloff, but also allows me to define vertical gradient colors (just 2: from-to). Working with URP, got it working as post processing effect, runs very smooth in WebGL, even on mobile, but seems to heavy to run in standalone VR. Very strange.... idea's?
You could add a custom fog pass in a shader, I think it's much easier than dealing with unity's fog as I just don't like the native's controls and can control all my behaviors in one script and update to all cams and whatnot easily
In the case of URP it's fairly easy to make for "spherical". Embed UniversalRP package to be able to modify it. Navigate to "Packages/$UniversalRP$/ShaderLibrary/ShaderVariablesFunctions.hlsl" Find InitializeInputDataFog function and change it to this: Code (CSharp): real InitializeInputDataFog(float4 positionWS, real vertFogFactor) { real fogFactor = 0.0; #if defined(_FOG_FRAGMENT) #if (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)) // Compiler eliminates unused math --> matrix.column_z * vec //float viewZ = -(mul(UNITY_MATRIX_V, positionWS).z); unity-provided implementation float viewZ = length(mul(UNITY_MATRIX_V, positionWS)); // spherical fog factor // View Z is 0 at camera pos, remap 0 to near plane. float nearToFarZ = max(viewZ - _ProjectionParams.y, 0); fogFactor = ComputeFogFactorZ0ToFar(nearToFarZ); #endif #else fogFactor = vertFogFactor; #endif return fogFactor; }