Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Shader "Unlit/MeshDistanceField"
- {
- Properties
- {
- _MeshDistanceField ("Mesh Distance Field", 3D) = "white" {}
- _CutoutField ("Cutout Distance Field", 3D) = "white" {}
- _MainTex("Main Texture", 2D) = "white" {}
- _NormalMap("Normal Map", 2D) = "blue" {}
- _Tiling("Main Texture Tiling", Float) = 1
- _NormalStrength("Normal Map Influence", Range(0,1)) = 0.5
- _CutOff ("Distance Field Collision", Range(0,0.1)) = 0.1
- _Gloss ("Gloss", Range(1,64)) = 4
- _ReflectionInfluence("Reflection Influence", Range(0,1)) = 0.5
- _AmbientInfluence("Ambient Influence", Range(0,1)) = 0.5
- _SpecularIntensity("Specular Intensity", Range(0,1)) = 0.5
- }
- SubShader
- {
- Tags
- {
- "RenderType"="Opaque"
- "LightMode"="ForwardBase"
- }
- LOD 100
- ZTest Always
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
- #include "UnityLightingCommon.cginc" // for _LightColor0
- struct appdata
- {
- float4 vertex : POSITION;
- };
- struct v2f
- {
- float4 vertex : SV_POSITION;
- float4 objectPosition : TEXCOORD0;
- float3 worldPosition : TEXCOORD1;
- };
- sampler3D _MeshDistanceField;
- sampler3D _CutoutField;
- float _CutOff;
- float _Tiling;
- float _NormalStrength;
- float _Gloss;
- float _AmbientInfluence;
- float _ReflectionInfluence;
- float _SpecularIntensity;
- sampler2D _MainTex;
- sampler2D _NormalMap;
- v2f vert (appdata v)
- {
- v2f o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.objectPosition = v.vertex;
- o.worldPosition = mul(unity_ObjectToWorld, v.vertex);
- return o;
- }
- float opU( float d1, float d2 )
- {
- return min(d1,d2);
- }
- float opS( float d1, float d2 )
- {
- return max(-d1,d2);
- }
- float map( in float3 pos ) {
- float d1 = tex3D(_MeshDistanceField, pos ).a;
- //float d2 = tex3D(_CutoutField, pos ).a * 2 - 1;
- return d1;//opS(d1,d2);
- }
- float3 calcNormal(in float3 pos)
- {
- // epsilon - used to approximate dx when taking the derivative
- const float2 eps = float2(0.1, 0.0);
- // The idea here is to find the "gradient" of the distance field at pos
- // Remember, the distance field is not boolean - even if you are inside an object
- // the number is negative, so this calculation still works.
- // Essentially you are approximating the derivative of the distance field at this point.
- float3 nor = float3(
- map(pos + eps.xyy) - map(pos - eps.xyy),
- map(pos + eps.yxy) - map(pos - eps.yxy),
- map(pos + eps.yyx) - map(pos - eps.yyx));
- return normalize(nor);
- }
- fixed4 calcPixelColor(float3 nop, float3 rayOrigin, float3 viewDir) {
- //get normal by sampling at various points around and integrating
- //Stole this from the Raymarching Unity tutorial, but had to significantly "fudge" (increase) the step offset
- // which I think is because analytical distance fields yield good results at small deltas, but mesh distance fields really don't
- float3 localNormal = calcNormal( nop * 1.75 - .375 );
- //convert to a "smooth" worldNormal
- float3 worldNormal = normalize(mul(unity_ObjectToWorld, localNormal).xyz);
- //triplanar normal map, based on smooth worldNormal
- fixed4 x = tex2D(_NormalMap, rayOrigin.yz * _Tiling);
- fixed4 y = tex2D(_NormalMap, rayOrigin.xz * _Tiling);
- fixed4 z = tex2D(_NormalMap, rayOrigin.xy * _Tiling);
- float3 blend = abs(worldNormal);
- blend *= blend;
- float3 normTex = UnpackNormal( blend.x * x + blend.y * y + blend.z * z );
- //apply normal distortion to object-space normal, then convert to world space for "final" (perturbed) normal
- localNormal.rg += normTex.rg * _NormalStrength;
- float3 finalNormal = normalize(mul(unity_ObjectToWorld, normalize(localNormal)));
- //get world position of pixel, make sure to use float4 object position, otherwise translation is ignored
- float4 worldPixel = mul(unity_ObjectToWorld, float4(rayOrigin,1));
- //for directional lights, this is the light dir
- float3 lightDir = _WorldSpaceLightPos0;
- float lighting = ( dot(finalNormal, lightDir ) * .5 + .5 ) * _LightColor0; //smooth lighting (Blinn?)
- //triplanar map, re-using previous vars
- x = tex2D(_MainTex, rayOrigin.yz * _Tiling);
- y = tex2D(_MainTex, rayOrigin.xz * _Tiling);
- z = tex2D(_MainTex, rayOrigin.xy * _Tiling);
- blend = abs(worldNormal);
- blend *= blend;
- fixed4 tex = blend.x * x + blend.y * y + blend.z * z;
- //Specular
- //Stole this from a shader forge graph example, because the "half vector" thing wouldn't work properly...
- float3 viewRefl = reflect(viewDir, worldNormal);
- //Intensity of the specular light
- float NdotH = max(dot( lightDir, viewRefl),0);
- float specIntensity = pow( saturate( NdotH ), _Gloss );
- //Stole this from the examples page
- // sample the default reflection cubemap, using the (world)reflection vector
- float3 worldRefl = reflect(viewDir, finalNormal);
- half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, worldRefl);
- // decode cubemap data into actual color
- half3 skyColor = DecodeHDR(skyData, unity_SpecCube0_HDR);
- //start with lit diffuse
- half4 col = lighting * tex;
- //add ambient term (stole this from the Unity manual again)
- col.rgb += ShadeSH9(half4(finalNormal,1)) * _AmbientInfluence;
- //reflectivity implemented as a lerp between diffuse and reflection
- fixed4 base = lerp(col, half4(skyColor,1), _ReflectionInfluence);
- //add specular afterwards, so even fully reflective objects retain their own specular
- base.rgb += specIntensity * _SpecularIntensity * tex.rgb;
- return base;
- }
- fixed4 frag (v2f i) : SV_Target
- {
- //TODO
- //1. determine the ray starting position from pixel object position & normal
- //object position determines volume pixel to read from (at starting edge)
- float3 nop = ( i.objectPosition.xyz + 1 ) * .5;
- //2. get starting "closest distance" value from outer edge of mesh distance field
- float minDist = map( nop * 1.75 - .375 );
- float3 rayOrigin = i.objectPosition.xyz;
- float3 viewDir = normalize( i.worldPosition - _WorldSpaceCameraPos.xyz );
- float3 rayDir = normalize( mul( unity_WorldToObject, viewDir ) );
- //ray march
- //3. raymarch into the mesh distance field from ( origin - viewDir ), until output < ..., or we leave bounds of object
- for( int step = 0; step < 64; ++step ) {
- rayOrigin += rayDir * minDist;
- nop = ( mul( unity_WorldToObject, rayOrigin) + 1 ) * .5;
- //out of bounds condition
- if ( min( min(nop.x, nop.y), nop.z) < 0 ) discard;
- if ( max( max(nop.x, nop.y), nop.z) > 1 ) discard;
- minDist = map(nop * 1.75 - .375);
- //4. output color based on distance of ray (calculate normal direction & light it?)
- if ( minDist < _CutOff ) {
- return calcPixelColor(nop, rayOrigin, viewDir);
- }
- }
- discard;
- return 0;
- }
- ENDCG
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement