基于次表面散射(SSS)的玉石渲染

本文效果图:

基于次表面散射(SSS)的玉石渲染_第1张图片

次表面散射(SSS)的概念自行百度,表现光进入物体,然后发生内部散射,最终从不同的位置射出,主要用于模拟玉石,翡翠,蜡烛,皮肤等有一点点透光的物体。常规的漫反射计算公式为:

float diffuse=max(0,dot(L,N));

《GPU GERM》中对使用环绕光照的方式模拟简单次表面散射,使原来为黑的地方有一点点光,公式为:

float wrap_diffuse=max(0,(dot(L,N)+wrap)/(1+wrap)); L 光照方向, N 表面法线,wrap为可变换参数

然后就是计算物体的半透明度,这里的透明只是模拟颜色的淡化,而非真的透明。物体半透的主要因素是光在物体中穿过的时候会逐渐丢失能量,穿过的物体越厚,越不透明,模拟半透主要问题在于获取光穿过物体距离。有两种计算方式:计算当前视角下场景的深度,然后以光的位置和方向做为摄像机位置和朝向渲染场景,获得光位置处场景的深度图,最后将光位置处深度信息映射到当前视角下,获得当前视角下光穿过物体的厚度;先渲染当前视角的深度信息,然后渲染当前视角背面的深度信息,获得物体当前视角下的厚度(这里面获取深度信息的原理,在另一篇说)。

首先采用前后摄像机的方式计算物体厚度,根据深度渲染模型的代码如下:

Shader "Custom/JadeRender2"
{
	Properties{
		_Base("Base", Color) = (1, 1, 1, 1)
		_Diffuse("Diffuse", Color) = (1, 1, 1, 1)
		_Specular("Specular",Color) = (1.0,1.0,1.0,1.0)
		_Shinness("Shinness",Range(8,256)) = 128
		_Wrap("Wrap",Range(0,1)) = 0.5
		_ScatterWidth("_ScatterWidth",Vector) = (0,0,0,0)
		_ScatterFactor("_ScatterFactor",Range(0,1)) = 0.75
	
	}
		SubShader{
		Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }
		Pass{
		Tags{ "LightMode" = "ForwardBase" }
		CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "Lighting.cginc"

		fixed4 _Diffuse;
	float _Wrap;
	float4 _ScatterWidth;
	float4 _Base;
	float _ScatterFactor;
	float4 _Specular;
	float _Shinness;

	struct a2v {
		float4 vertex : POSITION;
		float3 normal : NORMAL;
		float4 tangent:TANGENT;
		float2 texcoord:TEXCOORD0;
	};

	struct v2f {
		float4 pos : SV_POSITION;
		float3 wNormal : TEXCOORD0;
		float4 wPos:TEXCOORD1;
		float4 uv:TEXCOORD2;
		float4 scrPos:TEXCOORD3;
	};
	sampler2D _BackDepthTex;
	float4x4 _WolrdtoLightMatrix;
	float4x4 _LightTexMatrix;
	float _CamNearPlane;
	float _CamFarPlane;
	v2f vert(a2v v) {
		v2f o;
		o.pos = UnityObjectToClipPos(v.vertex);
		o.wNormal = mul(v.normal, (float3x3)unity_WorldToObject);
		o.scrPos = ComputeScreenPos(o.pos);
		o.wPos = mul(unity_ObjectToWorld,v.vertex);
		return o;
	}
	
	fixed4 frag(v2f i) : SV_Target{

	float d_o = mul(_WolrdtoLightMatrix,i.wPos).z;
	d_o = 1-(-d_o - _CamNearPlane) / (_CamFarPlane - _CamNearPlane);

	float4 tpos = mul(_LightTexMatrix,i.wPos);
	float4 scrPos = tpos * 0.5f;
	scrPos.xy = float2(scrPos.x, scrPos.y * 1) + scrPos.w;
	scrPos.zw = tpos.zw;

	float4 backDepthColor = tex2D(_BackDepthTex, i.scrPos.xy / i.scrPos.w); // SAMPLE_DEPTH_TEXTURE(_BackDepthTex, UNITY_PROJ_COORD(scrPos)); //tex2D(_BackDepthTex, i.scrPos.xy / i.scrPos.w); //
	float d_i = -(backDepthColor*(_CamFarPlane - _CamNearPlane) + _CamNearPlane);
	d_i = backDepthColor.r;

	float depth = d_o - d_i;
	float3 scattering = exp(-_ScatterWidth.xyz*depth*10);


	float3 N = normalize(i.wNormal);

	float4 texcol = float4(1.0,1.0,1.0,1.0);
	fixed3 albedo = texcol.rgb*_Diffuse.rgb;
	fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
	float3 L = normalize(UnityWorldSpaceLightDir(i.wPos));
	float3 V = normalize(UnityWorldSpaceViewDir(i.wPos));
	float3 H = normalize(L + V);

	float wrap = (dot(L,N) + _Wrap) / (1 + _Wrap);
	float wrap_diff = max(0,wrap);
	fixed3 diffuse = _LightColor0.rgb * wrap_diff*albedo;
	float s = pow(max(0,dot(N,H)),_Shinness);
	float3 specular = _LightColor0.rgb *_Specular.rgb*s;
	float4 finCol = float4(0,0,0,0);
	finCol.rgb = _Base*(ambient + diffuse)*scattering+ specular;
	finCol.a = texcol.a;
	return finCol;
	}

		ENDCG
	}
	}
		// FallBack "Diffuse"
}

源码地址:

https://download.csdn.net/download/wang371372/10775296

   参考两位大佬的文章

https://blog.csdn.net/z18636930051/article/details/75105422

http://gad.qq.com/article/detail/19080

你可能感兴趣的:(unity,3d)