效果图:
使用phong着色模型,将环境光、物体的漫反射光、镜面光三种光效加合而得到上图的效果
raymarch 的shader代码:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "RayMarch/Primitives1_phong" { Properties { _MainTex("Texture", 2D) = "white" {} } SubShader { Tags{ "RenderType" = "Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" #define PointLightPos fixed3(0, 3, 0) #define LightColor float3(1,1,1) #define GlobalAmibent float3(0.1,0.1,0.1) 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, -0.5), 0); float2 ball_1 = float2(sdSphere(pos, float3(0, 0, 2), 0.5), 1); float2 ball_2 = float2(sdSphere(pos, float3(2, 0, 5), 0.5), 2); float2 ball_3 = float2(sdSphere(pos, float3(-2, 0, 5), 0.5), 3); //求物体的并集 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 for (int i = 0; i<64; 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 = -1.0; return float2(t, m); } //计算碰撞点处物体表面的法线 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); } float3 BasicPhong(fixed3 lightDir, fixed3 normalDir, fixed3 viewDir, int matIndex) { float Ka = 1, Kd = 1, Ks = 1, Shininess = 7; float3 matColor = float3(0.5, 0.5, 0); switch (matIndex) { case -1: //极远处 matColor = float3(0, 0, 0); break; case 0: matColor = float3(0.1, 0.1, 0.1); break; case 1: matColor = float3(0.5, 0.5, 0); Shininess = 30; break; case 2: matColor = float3(0, 0.5, 0.5); break; case 3: matColor = float3(0.5, 0, 0.5); break; default: break; } float3 amibent = Ka * GlobalAmibent * matColor; float3 diffuse = Kd * LightColor * clamp(dot(normalDir, lightDir), 0, 1) * matColor; fixed3 reflectDir = reflect(-lightDir, normalDir); float3 specular = Ks * LightColor * pow(clamp(dot(viewDir, reflectDir), 0, 1), Shininess) * matColor; float3 color = amibent + diffuse + specular; return color; } 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 col = BasicPhong(lightDir, nor, rd, m); return col; } 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(0,0,-1); //投影面某点坐标 float3 p = float3(i.uv - float2(0.5, 0.5),0); //投掷射线 float3 rd = normalize(p - ro); fixed4 col = fixed4(render(ro, rd).rgb, 1); // gamma校正 col.rgb = pow(col.rgb, float3(0.4545, 0.4545, 0.4545)); return col; } ENDCG } } }