思路:利用平面的顶点和法线模拟一束平行光线,计算光线与物体(AABB)相交部分,以一定的步长采样相交部分,并且累加每一步采样得到的值,最后乘以相同的系数取平均值输出颜色。
Shader "Custom/XShader"
{
Properties
{
_Volume("Texture3D", 3D) = "" {}
lightScale("LightScale",Range(0,5)) = 1
LightColor("LightColor",color) = (0,0,0,1)
Zvalue("value",Range(0,1)) = 0
}
SubShader
{
Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler3D _Volume;
float lightScale;
float4x4 Direction;
fixed4 LightColor;
float Zvalue;
bool cubeIntersection(float3 origin, float3 direction, float3 aabbMax, float3 aabbMin, out float tNear, out float tFar)
{
float3 t1 = direction * (aabbMax - origin);
float3 t2 = direction * (aabbMin - origin);
float3 tMin = min(t1, t2);
float3 tMax = max(t1, t2);
tNear = min(max(tMin.x, tMin.y), tMin.z);
tFar = max(min(tMax.x, tMax.y), tMax.z);
return tNear <= tFar;
}
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 Pos : TEXCOORD0;
float3 Normal : TEXCOORD1;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.Pos = v.vertex;
o.Normal =normalize(v.normal);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
float4 col = float4(0, 0, 0, 0);
float3 lpos = i.Pos;
float3 ldir = i.Normal;
float3 origin = lpos - ldir;//投影算法需要的参数
float tNear, tFar;
cubeIntersection(origin, ldir, float3(0.5, 0.5, 0.5), float3(-0.5, 0.5, -0.5), tNear, tFar);//AABB投影算法
float CurrentRayLength = tNear;
float maxSamples = 512;//我的Texture3d是512*512*176的
float CurrentSample = 0;
float Steplength = 1.732 / maxSamples; //box两点间最大距离/max
int CurrentStep = 0;
while (CurrentRayLength<tFar)
{
float3 pos = mul(Direction, origin + ldir*CurrentRayLength);
pos += 0.5; //-0.5-0.5=>0-1
float4 pcol = tex3Dlod(_Volume, float4(pos, 0));//将采样数据投影到面片
if (pcol.a >= Zvalue)
{
CurrentStep += 1;
float single = (pcol.a - Zvalue) / (1 - Zvalue) * (maxSamples - CurrentSample) / maxSamples;//关于pcol.a直线方程乘以采样衰减系数
col.a += single; //叠加
}
CurrentRayLength += Steplength;
CurrentSample += 1;
}
if (CurrentStep>0) {
col.a = col.a *0.08;//以相同的标准取平均值,毕竟加了那么多次
}
return float4(col.aaa,1)*LightColor*lightScale;//加上光的强度和颜色
}
ENDCG
}
}
}