效果图:
在RayMarch渲染算法中计算阴影非常简单,当射线碰撞到物体,从碰撞点逆着光源方向再次执行射线步进算法,如果这一过程中碰撞到了其他物体,则碰撞点被其他物体遮挡,该处具有阴影。
shader代码:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "RayMarch/Primitives1_phong" { Properties { _MainTex("Texture", 2D) = "white" {} } SubShader { Tags{ "RenderType" = "Opaque" } LOD Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" #define PointLightPos fixed3(0, 6, 0) #define LightColor float3(1,1,1) #define GlobalAmibent float3(0.01,0.01,0.01) struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; //################################################################################### //sdf:define primitives float sdPlane(float3 p,float planeYPos) { return p.y - planeYPos; } float sdSphere(float3 p, float3 spherePos, float radius) { return length(p - spherePos) - radius; } //################################################################################### //################################################################################### //primitives boolean operation //求并集 float2 opU(float2 d1, float2 d2) { return (d1.x<d2.x) ? d1 : d2; } //################################################################################### // define the scene float2 map(in float3 pos) { //plane //float2(x,y)的第二个参数定义了该物体的材质id,在render环节,可以根据id做不同处理 float2 plane = float2(sdPlane(pos, -); float2 ball_1 = float2(sdSphere(pos, float3(, , ), ); float2 ball_2 = float2(sdSphere(pos, float3(, , ), ); float2 ball_3 = float2(sdSphere(pos, float3(-, , ), ); //求物体的并集 float2 res = opU(opU(opU(ball_1, ball_2), ball_3),plane); return res; } float2 castRay(in float3 ro, in float3 rd) { float tmin = 1.0; //射线最大允许经过的距离 float tmax = 100.0; ; //当前已经过的距离 float t = tmin; //材质id float m = -1.0; //最大迭代次数定位64 ; i<; i++) { //距离精度随距离的增加而减小 float precis = 0.0005*t; //获得场景中物体距离该点的距离,及距离最近物体的材质id float2 res = map(ro + rd*t); //如果与场景物体发生碰撞,或者射线行进距离超出最大范围,则跳出迭代 if (res.x<precis || t>tmax) break; t += res.x; m = res.y; } if (t > tmax) { m = -clamp((t - tmax) / (tmaxmax - tmax), , ); } return float2(t, m); } int IsInShadow(in float3 ro, in float3 rd) { ; float tmax = distance(ro, PointLightPos); ; ; i < ; i++) { float precis = 0.0005*t; float2 res = map(ro + rd*t); if (res.x < precis) { result = ; break; } if (t >= tmax) { result = ; break; } t += res.x; } return result; } //计算碰撞点处物体表面的法线 float3 calcNormal(in float3 pos) { float3 eps = float3(0.0005, 0.0, 0.0); float3 nor = float3( map(pos + eps.xyy).x - map(pos - eps.xyy).x, map(pos + eps.yxy).x - map(pos - eps.yxy).x, map(pos + eps.yyx).x - map(pos - eps.yyx).x); return normalize(nor); } void BasicPhong(fixed3 lightDir, fixed3 normalDir, fixed3 viewDir, float matIndex, out float3 color1, out float3 color2) { , Kd = , Ks = , Shininess = ; float3 matColor = float3(); switch (matIndex) { : //极远处 matColor = float3(, , ); Ka = ; Ks = ; Kd = ; break; : matColor = float3(0.1, 0.1, 0.1); Ks = ; break; : matColor = float3(); Shininess = ; break; : matColor = float3(, 0.5, 0.5); break; : matColor = float3(, 0.5); break; default: break; } && matIndex >-) { Kd = lerp(Kd, , -matIndex); Ks = lerp(Ks, , -matIndex); Ka = lerp(Ka, , -matIndex); } float3 amibent = Ka * GlobalAmibent * matColor; float3 diffuse = Kd * LightColor * clamp(dot(normalDir, lightDir), , ) * matColor; fixed3 reflectDir = reflect(-lightDir, normalDir); float3 specular = Ks * LightColor * pow(clamp(dot(viewDir, reflectDir), , ), Shininess) * matColor; color1 = amibent; color2 = diffuse + specular; } float3 render(in float3 ro, in float3 rd) { //投掷射线,获得与所场景物体的碰撞信息 float2 res = castRay(ro, rd); float t = res.x; float m = res.y; float3 pos = ro + t*rd; float3 nor = calcNormal(pos); fixed3 lightPos = PointLightPos; fixed3 lightDir = normalize(lightPos - pos); float3 c1, c2; BasicPhong(lightDir, nor, rd, m, c1, c2); float isInShadow = IsInShadow(pos, lightDir); float3 color = c1 + isInShadow * c2; return color; } v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag(v2f i) : SV_Target { //虚拟摄像机坐标 float3 ro = float3(,,-); //投影面某点坐标 float3 p = float3(i.uv - float2(); //投掷射线 float3 rd = normalize(p - ro); fixed4 col = fixed4(render(ro, rd).rgb, ); // gamma校正 col.rgb = pow(col.rgb, float3(0.4545, 0.4545, 0.4545)); return col; } ENDCG } } }