效果图:
将下面的shader代码对应的Material拖给一个面片,即可看到效果。
shader代码:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "RayMarch/Primitives0_diffuse" { 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" 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), 1); float2 ball_1 = float2(sdSphere(pos, float3(0, 0, 2), 0.5), 2); 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), 2); //求物体的并集 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 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); float3 ref = reflect(rd, nor); //漫反射着色 fixed3 lightPos = fixed3(0, 3, 0); fixed3 lightDir = normalize(lightPos - pos); float dif = clamp(dot(nor, lightDir),0, 1); float3 col = float3(dif, dif, dif); return float3(clamp(col, 0.0, 1.0)); } 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 } } }