用代码来画画 —— Ray-Marching(光线步进)【Unity Shader】

来源:互联网 发布:百度大数据交响乐 编辑:程序博客网 时间:2024/05/01 12:20


参考自:

http://blog.csdn.net/baidu_26153715/article/details/46510703

http://imgtec.eetrend.com/blog/8845

http://ogldev.atspace.co.uk/www/tutorial13/tutorial13.html


效果如图:





完整代码及详细注释如下【修正了源代码的一些错误】:

Shader "Custom/RayMarching"{Properties{//_MainTex ("Texture", 2D) = "white" {}//_Cube("cubemap", cube) = ""{}}// 代码参考自 http://blog.csdn.net/baidu_26153715/article/details/46510703SubShader{// No culling or depthCull Off ZWrite Off ZTest AlwaysPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"// 球体 [Signed Distance Function]float sdfSphere(float3 p, float s){return length(p) - s;}// 立方体float sdfBox(float3 p, float3 b){return length(max(abs(p) - b, 0.));}// p 从相机空间换算到世界坐标系(即世界坐标系下光线步进的方向)float3 CameraSpace2WorldSpace(in float3 camPos, in float3 p){float3 target = float3(0, 0, 0);float3 AxisY = float3(0, 1, 0);float3 z = normalize(target - camPos); // lookfloat3 x = normalize(cross(z, AxisY)); // rightfloat3 y = normalize(cross(z, x)); // up                                // 注意:(right, up, look) * world = camera,Cg 矩阵是按行存储float3 theWorldSpaceP = float3(// dot(p, x), dot(p, y), dot(p, z)//p.x*x + p.y*y + p.z*z//mul(float3x3(x, y, z), p)mul(p, float3x3(x, y, z)) // 交换相乘位置相当于转置);return  theWorldSpaceP;}// 步进的光线终点和球体表面的距离float map(in float3 pos){//float d = sdfSphere(pos, 1);float d = sdfBox(pos, float3(.5, .5, .5));return d;}float3 normal(in float3 pos){float2 offset = float2(.01, 0);float3 nDir = normalize(float3(// 通过计算 xyz 三个方向的差值(梯度),归一化后得到法线方向map(pos + offset.xyy) - map(pos - offset.xyy),map(pos + offset.yxy) - map(pos - offset.yxy),map(pos + offset.yyx) - map(pos - offset.yyx)));return nDir;}float marching(in float3 origin, in float3 dir){float t = 1;// 步进的光线总长度int i;for (i = 0; i<64; ++i){// 随着光线的步进,检查是否到达球体的表面float3 graphic = origin + t*dir;float d = map(graphic);// 当距离小于一个最小阈值,或者长度超过一个最大阈值,则中断循环if (d < .02 || t>20)break;// 步进t += d;}return t;}float3 render(in float3 pos, in float3 p){// 距离辅助的 ray-marchingfloat d = marching(pos, p);// 计算法线方向(世界坐标系)float3 nDir = normal(pos + p*d);float3 c = 0;if (d<30){// 光源方向(世界坐标系)float3 lDir = normalize(half3(0, 1, 0));// diffusefloat diff = max(0, dot(lDir, nDir));// 将法线方向作为颜色返回c = nDir;}return c;}struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};v2f vert (appdata v){v2f o;o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);o.uv = v.uv;return o;}//sampler2D _MainTex;fixed4 frag (v2f i) : SV_Target{float time = _Time.y;float2 uv = i.uv * 2 - 1;                                // 相机坐标系float3 p = normalize(float3(uv, 2));// 相机的世界坐标(随时间变化)float3 camPos = float3(3 + sin(time), 3, 3 + cos(time));// 将 p 点换从相机坐标系换算到世界坐标系float3 theNewP = CameraSpace2WorldSpace(camPos, p);// 渲染模型的法线fixed3 col = render(camPos, theNewP);return fixed4(col, 1);}ENDCG}}FallBack "Diffuse"}


加上光照计算的效果:






取 并集(Union) 的效果:



实际应用:用 SDF 和 Raymarch 让代码 “画画”



1 0
原创粉丝点击