使用Unity簡單實現RayMarching

使用Unity簡單實現RayMarching

前言

最近在玩ShaderToy,發現各種酷炫的效果都是隻用數學公式計算出來的,心中膜拜之情溢於言表同時也深深自省:爲啥不好好學數學呢😂。
附上ShaderToy鏈接:https://www.shadertoy.com/

效果

簡單的使用RayMarching實現了一個蘭伯特模型,接下來應該會持續研究RayMarching的😀
在這裏插入圖片描述

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章