使用Unity简单实现RayMarching

使用Unity简单实现RayMarching

前言

最近在玩ShaderToy,发现各种酷炫的效果都是只用数学公式计算出来的,心中膜拜之情溢于言表同时也深深自省:为啥不好好学数学呢。
附上ShaderToy链接:https://www.shadertoy.com/

效果

简单的使用RayMarching实现了一个兰伯特模型,接下来应该会持续研究RayMarching的
使用Unity简单实现RayMarching_第1张图片

RayMarching算法思想

从相机发射n条射线,射线有一个采样的步长。当射线处在体纹理中时,每个步长采一次样,获取纹理值(实际上表示该点的密度值),计算光照,然后和该条射线当前累积的颜色值进行混合。

关于Ray tracing、Ray marching 、Ray casting的区别可以看看这个链接
https://www.zhihu.com/question/29863225

实现步骤

首先我们看一下RayMarch的过程,下面是pixel shader部分

float4 frag (v2f i ) : SV_TARGET
{
	float3 worldPosition = i.worldPos;
	float3 viewDirection = normalize(i.worldPos - _WorldSpaceCameraPos.xyz);
	float3 lightDirection = normalize(UnityWorldSpaceLightDir(i.worldPos));
	return raymarch (_WorldSpaceCameraPos.xyz,viewDirection,lightDirection);
}

可以看到RayMarch需要一个发射起始点rayOrigin和一个光线方向rayDirection,通过DistanceFunction方法绘制形状

float4 raymarch (float3 rayOrigin, float3 rayDirection,float3 lightDir)
{
	for (int i=0; i<256; i++)
	{
		float ray = DistanceFunction(rayOrigin);
		if(_Limit != 0)
		{
		    if (distance(rayOrigin,ray*rayDirection)>250) 
		    break;
		}
		if (ray < _MinDistance) 
			return float4 (lighting(rayOrigin,lightDir,rayDirection),1.0); 
		else 
			rayOrigin+=ray*rayDirection; 
	}
	return float4 (0.0,0.0,0.0,0.0);
}

这里我就画了个最简单的球

float DistanceFunction (float3 p)
{
	return length(p)-1;
}

当光线与模型撞击时返回一个lighting()方法,否则就继续前进

if (ray < _MinDistance) 
	return float4 (lighting(rayOrigin,lightDir,rayDirection),1.0); 
else 
	rayOrigin+=ray*rayDirection; 

lighting部分,简单的兰伯特光照模型

float3 lighting (float3 rayOrigin,float3 lightDir,float3 viewDir)
{
	float3 AmbientLight = float3 (0.1,0.1,0.1);
	float3 LightDirection = normalize(lightDir);
	float3 NormalDirection = set_normal(rayOrigin);
	float3 LightColor = _LightColor0.rgb;
	return ( max(dot(LightDirection, NormalDirection) * 0.8 + 0.15,0.0) * LightColor + AmbientLight) * ambient_occlusion(rayOrigin,NormalDirection);
}

设置法线方向

float3 set_normal (float3 p)
{
	float3 x = float3 (0.0001,0.00,0.00);
	float3 y = float3 (0.00,0.0001,0.00);
	float3 z = float3 (0.00,0.00,0.0001);
	return normalize(float3(DistanceFunction(p+x)-DistanceFunction(p-x), DistanceFunction(p+y)-DistanceFunction(p-y), DistanceFunction(p+z)-DistanceFunction(p-z))); 
}

AO部分

float ambient_occlusion( float3 pos, float3 nor )
{
	float occ = 0.0;
	float sca = 1.0;
	for( int i=0; i<5; i++ )
	{
		float hr = 0.01 + 0.12*float(i)/4.0;
		float3 aopos =  nor * hr + pos;
		float dd = DistanceFunction(aopos);
		occ += -(dd-hr)*sca;
		sca *= 0.95;
	}
	return clamp( 1.0 - 3.0*occ, 0.0, 1.0 );    
}

参考

[1] https://www.jianshu.com/p/46e161b911dd
[2] https://www.zhihu.com/question/29863225
[3] https://www.shadertoy.com/view/lt33z7

你可能感兴趣的:(Unity3D,Unity,Shader,Shader)