unity shader [gamma]_Marmoset眼球效果在Unity和UE4中的实现

ad18c95aaec49ed029adc5e3b044eff9.png

前言:很早之前美术有个眼球效果的需求,然后扔给我一张原画图,让我照着做,前期并没有太多的需求,唯一的需求就是要像,越像越好!好吧,我是美术的搬运工,让我做啥我做啥,观察了一下原画,发现原画眼睛的效果偏迪士尼卡通风格,写实但又很夸张,所以我并没有走基于物理渲染的那一套光照模型,而是基与艺术导向来写shader,这样写的好处是用最简单的光照模型模拟出最贴近原画的效果,但坏处是不稳定,完全不遵循物理守恒,在不同的环境下可能出现不协调的现象,最后实现效果如下:

835b90c29208577555ab538cc55a67aa.png

这种效果差不多扛了大半年,最后要改,要提升效果,美术说要晶莹剔透的感觉,尤其是侧面感觉太平,好吧,之前没有考虑进去折射,因为之前考虑到移动平台,眼睛在游戏里面不会拉的太近,看到的只能是个形状和一个高光点所以把这部分的消耗给去掉了,但是现在某些情况会看的很近,所以不带折射的眼球的弊端就暴露了出来,所以整理了下思路,查找了很多眼球的资料尤其这篇

大星星:写实角色眼睛的制作​zhuanlan.zhihu.com
4ee79cf99a727cc1ff8b5199f7a9f7ec.png

核心理论知识说的很全面,我就不重复了,我只分享一下实现的过程!其实核心就是折射的实现,在正面瞳孔要有往里凹的感觉,在侧面因为折射,能看见完整形变的瞳孔效果,最终效果如下:

d6b9715ad954f65282a3caf2a8592ca3.gif
Unity中实现的效果

42dd050ad00df3c1fd1a44427c61eb00.gif
UE实现的效果

折射部分主要利用了视差图,理论部分:

d07c4f5f5ff070a2b12026b3ffdf4ae0.png

个人理解:因为viewDir是在切线空间的(xy与uv对齐),所以viewDir.xy对应的就是offset

所以由简单的比例关系推导出offset值:

但是这种方法对于平面的物体得出的视差结果较为精确,对于曲面的物体,会有1,2之间的误差

b7603446798b01ba565f7bb27b6c9b6f.png

恰巧我们要制作的眼球就是曲面的球体,得出的结果美术们肯定不满意,连我自己这关都过不了,所以就去各大成熟渲染引擎偷师了一下,发现Marmoset这款引擎对眼球的折射处理的非常好,如下所示:

1a04666d6d0208172935ded3c5af90d8.png

折射效果正是我们想要的,扒开源代码发现前面思路是一样的,只是后面在修正误差的时候,采用的是比Parallax Map,更精确的Relief Map,通过线性步进法逐步逼近,得出最真实的高度,减少曲面的误差,翻译成unity代码如下:

void SurfaceParallaxMap(inout FragmentState s)
{
    half3 dir = half3(dot(-s.vertexEye, s.vertexTangent),
					dot(-s.vertexEye, s.vertexBitangent),
					dot(-s.vertexEye, s.vertexNormal));
    half2 maxOffset = dir.xy * (_uParallaxDepthOffset / (abs(dir.z) + 0.001));

    float minSamples = 12.0;
    float maxSamples = 12.0;
    float samples = saturate(3.0 * length(maxOffset));
    float incr = rcp(lerp(minSamples, maxSamples, samples));

    half2 tc0 = s.vertexTexCoord - _uParallaxDepthCenter * maxOffset;
    float h0 = ParallaxSample(tc0);
    for (float i = incr; i <= 1.0; i += incr)
    {
        half2 tc = tc0 + maxOffset * i;
        float h1 = ParallaxSample(tc);
        if (i >= h1)
        {
						//hit! now interpolate
            float r1 = i, r0 = i - incr;
            float t = (h0 - r0) / ((h0 - r0) + (-h1 + r1));
            float r = (r0 - t * r0) + t * r1;
            s.vertexTexCoord = tc0 + r * maxOffset;
            break;
        }
        h0 = h1;
    }

				//standard normal mapping
	SurfaceNormalMap(s);
}

UE4中custom节点和code

ee957fdce868cbe071d91f16a21196f7.png

d550525859f514e3497560b7bb51dde8.png
  half3 dir = tangentEye;
  half2 maxOffset = dir.xy * (uParallaxDepthOffset / (abs(dir.z) + 0.001));
 float minSamples = 12.0;
    float maxSamples = 12.0;
    float samples = saturate(3.0 * length(maxOffset));
    float incr = rcp(lerp(minSamples, maxSamples, samples));

    half2 tc0 = vertexTexCoord - uParallaxDepthCenter * maxOffset;
    float h0 = 1 - (Texture2DSample(mixMap,Material.Texture2D_0Sampler, tc0).r);
    for (float i = incr; i <= 1.0; i += incr)
    {
        half2 tc = tc0 + maxOffset * i;
        float h1 = 1 - (Texture2DSample(mixMap, Material.Texture2D_0Sampler,tc).r);
        if (i >= h1)
        {
						//hit! now interpolate
            float r1 = i, r0 = i - incr;
            float t = (h0 - r0) / ((h0 - r0) + (-h1 + r1));
            float r = (r0 - t * r0) + t * r1;
           vertexTexCoord = tc0 + r * maxOffset;
            break;
        }
        h0 = h1;
    }
  half2 UV=vertexTexCoord;
return UV;

美术的工具链:

一、模型部分:分eyeBall和eyeCornea的高模和低模

1、eyeBall的高模是一个往里的模型,低模是一个往外的模型。如图:

38bf517ebeb55f72072f6f20d1ccc76b.png

虹膜部分可以通过置换贴图来实现,如图所示:

2b02fcf924360b5df3e06eb5f62e68a4.png

2、eyeCornea高地模都是往外凸的模型,如图所示:

d01fb2e621bfb5ef3e17135827a1f559.png

二、贴图部分:

1、法线贴图:根据高模按个人爱好在maya,Xnormal,max中烘焙出eyeBall和eyeCornea高模的法线贴图

2、Albedo贴图:虽然网上有很多制作眼球的软件可以烘焙出相应的贴图,但是美术需要学习成本,最快的还是PS吧,随心所欲,不用学习

3、剩下的贴图有了这两张基础贴图,可以在SP中产出

最终会有六张贴图

5a18dbdd4d162ba130e30dff29f663e1.png
  1. Eyeball Normal
  2. Eyeball Gloss
  3. Parallax
  4. Eyeball Albedo
  5. Eyeball Spec
  6. Cornea Normal

Unity完整代码如下:

EyeCornea的shader:

Shader "M1Toolv5/MarmosetEyeCornea"
{

	Properties
	{

		[Header(Surface)]
	    _tNormalMap("Normal Map", 2D) = "bump" {}
		[Header(Microsurface)]

		_tGlossMap("Gloss Map", 2D) = "white"{}
		_Gloss("Gloss",Range(0,1)) = 0.088
		_uGlossHorizonSmooth("Horizon Smoothing",Range(0,1)) = 0.98

		[Header(Albedo)]

		[Header(Diffusion)]

		_ScatterDepth("Scatter Depth",Range(0,1)) = 0.98

		[Header(Reflectivity)]

		_tSpecularMap("Specular Map", 2D) = "white"{}
		_uSpecularColor("SpecularColor",Color) = (1,1,1,1)
		_SpecularMapIntensity("Intensity",Range(0,1)) = 0.088
		_Fresnel("Fresnel",Range(0,1)) = 0.98
		_uFresnelColor("FresnelColor",Color) = (1,1,1,1)

		[Header(Reflection)]
		_HorizonOcclusion("Horizon Occlusion",Range(0,1)) = 0.088
	
		[Header(EveReflection)]
		_Cubemap("CubeMap", CUBE) = ""{}
		_CubemapIntensity("CubemapIntensity",Range(0,8)) = 1
		_CubemapRotation("CubemapRotation",Range(-360,360)) = 1

		
	}
		SubShader
		{
			Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
			LOD 100
		


			Pass
			{

			Blend SrcAlpha One
			Name "FORWARD"
			Tags{ "LightMode" = "ForwardBase" }
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile __ M1_GAMMA_CORRECT
			#include "UnityCG.cginc"
			#include "Lighting.cginc"
            #include "AutoLight.cginc"
            #include "MarmosetEyeCG.cginc"
		 //   #include "m1v5Env.cginc"
	

       
			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
				float4 tangent : TANGENT;
				float3 normal : NORMAL;
			};

			struct v2f
			{
				float4 uv : TEXCOORD0;
				float4 pos : SV_POSITION;
				float3 normalDir : TEXCOORD1;
				float3 vDir : TEXCOORD2;
				float4 posWorld : TEXCOORD3;
				float3 tspace0 : TEXCOORD4;
				float3 tspace1 : TEXCOORD5;
				float3 tspace2 : TEXCOORD6;

			};

			
			v2f vert (appdata v)
			{
				v2f o;
				o.posWorld = mul(unity_ObjectToWorld, v.vertex);
				o.pos = UnityObjectToClipPos(v.vertex);
				o.normalDir = UnityObjectToWorldNormal(v.normal);
				o.vDir = normalize(_WorldSpaceCameraPos.xyz - o.posWorld.xyz);
				float3 wTangent = UnityObjectToWorldDir(v.tangent.xyz);
				float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
				float3 wBitangent = cross(o.normalDir, wTangent) * tangentSign;
				o.tspace0 = float3(wTangent.x, wBitangent.x, o.normalDir.x);
				o.tspace1 = float3(wTangent.y, wBitangent.y, o.normalDir.y);
				o.tspace2 = float3(wTangent.z, wBitangent.z, o.normalDir.z);
			
				o.uv.xy = v.uv;
				return o;
			}
			
      

			float4 frag (v2f i) : SV_Target
			{

				FragmentState s;

				s.vertexEye = i.vDir;
				s.vertexTangent = half3(i.tspace0.x, i.tspace1.x, i.tspace2.x);
				s.vertexBitangent = half3(i.tspace0.y, i.tspace1.y, i.tspace2.y);
				s.vertexNormal = i.normalDir;
				s.vertexTexCoord = i.uv.xy;

				s.tspace0 = i.tspace0;
				s.tspace1 = i.tspace1;
				s.tspace2 = i.tspace2;

				s.albedo=1;
				s.normal=1;
				s.gloss= _Gloss;
				s. reflectivity=1;
				s. fresnel=1;
				s. diffuseLight=0;
				s. specularLight=0;
				s. emissiveLight=0;


				LightParams l;
				l.color = _LightColor0.xyz;
				l.direction = normalize(_WorldSpaceLightPos0.xyz);
				l.attenuation = 1;
				l.shadow = 1;


		
				SurfaceNormalMap(s);
				AlbedoMap(s);
				ReflectivitySpecularMap(s);
				DiffusionLambertianLight(s, l);

				
				DiffusionLambertianEnv(s);
				
				ReflectionGGXLight(s, l);
				ReflectionGGXEnv(s);



				half3 fincol = s.diffuseLight + s.specularLight;

				
			
				return half4(fincol,1);



			}
			ENDCG
		}
	}
}

EyeBall部分的shader:

Shader "M1Toolv5/MarmosetEyeBall"
{

	Properties
	{

		[Header(Surface)]

	    _tNormalMap("Normal Map", 2D) = "bump" {}
	
		_uParallaxDepthOffset("Depth",Range(0,0.5)) = 0.088
		_uParallaxDepthCenter("Depth Center",Range(0,1)) = 0.98

		_mixMap("Mix Map", 2D) = "white"{}
		[Header(Microsurface)]

	
		_Gloss("Gloss",Range(0,1)) = 0.088
		_uGlossHorizonSmooth("Horizon Smoothing",Range(0,1)) = 0.98

		[Header(Albedo)]

		_tAlbedoMap("Albedo Map", 2D) = "white"{}
		_uAlbedoMapColor("AlbedoMap Color", Color) = (1,1,1,1)

		[Header(Diffusion)]

		_ScatterDepth("Scatter Depth",Range(0,1)) = 0.98

		[Header(Reflectivity)]


		_uSpecularColor("SpecularColor",Color) = (1,1,1,1)
		_SpecularMapIntensity("Intensity",Range(0,1)) = 0.088
		_Fresnel("Fresnel",Range(0,1)) = 0.98
		_uFresnelColor("FresnelColor",Color) = (1,1,1,1)

		[Header(Reflection)]
		_HorizonOcclusion("Horizon Occlusion",Range(0,1)) = 0.088

		[Header(SecondaryReflection)]
		_SecHorizonOcclusion("Secondary Horizon Occlusion",Range(0,4)) = 0.088
		_uGGXSecondaryGloss("Secondary Gloss",Range(0,1)) = 0.088
		_uGGXSecondaryIntensity("Secondary Instensity",Range(0,4)) = 0.088
		_uGGXSecondaryFresnel("Secondary Fresnel",Range(0,1)) = 0.088
		[Header(EveReflection)]
		_Cubemap("CubeMap", CUBE) = ""{}
	
		_CubemapIntensity("CubemapIntensity",Range(0,8)) = 1
		_CubemapRotation("CubemapRotation",Range(-360,360)) = 1

		
	}
		SubShader
		{
			Tags { "RenderType" = "Opaque" }
			LOD 100
	
			Pass
			{
			Name "FORWARD"
			Tags{ "LightMode" = "ForwardBase" }
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile __ M1_GAMMA_CORRECT
			#include "UnityCG.cginc"
            #include "MarmosetEyeCG.cginc"
		

       
			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
				float4 tangent : TANGENT;
				float3 normal : NORMAL;
			};

			struct v2f
			{
				float4 uv : TEXCOORD0;
				float4 pos : SV_POSITION;
				float3 normalDir : TEXCOORD1;
				float3 vDir : TEXCOORD2;
				float4 posWorld : TEXCOORD3;
				float3 tspace0 : TEXCOORD4;
				float3 tspace1 : TEXCOORD5;
				float3 tspace2 : TEXCOORD6;

			};

			
			v2f vert (appdata v)
			{
				v2f o;
				o.posWorld = mul(unity_ObjectToWorld, v.vertex);
				o.pos = UnityObjectToClipPos(v.vertex);
				o.normalDir = UnityObjectToWorldNormal(v.normal);
				o.vDir = normalize(_WorldSpaceCameraPos.xyz - o.posWorld.xyz);
				float3 wTangent = UnityObjectToWorldDir(v.tangent.xyz);
				float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
				float3 wBitangent = cross(o.normalDir, wTangent) * tangentSign;
				o.tspace0 = float3(wTangent.x, wBitangent.x, o.normalDir.x);
				o.tspace1 = float3(wTangent.y, wBitangent.y, o.normalDir.y);
				o.tspace2 = float3(wTangent.z, wBitangent.z, o.normalDir.z);
			
				o.uv.xy = v.uv;
				return o;
			}
			
      

			float4 frag (v2f i) : SV_Target
			{

				FragmentState s;

				s.vertexEye = i.vDir;
				s.vertexTangent = half3(i.tspace0.x, i.tspace1.x, i.tspace2.x);
				s.vertexBitangent = half3(i.tspace0.y, i.tspace1.y, i.tspace2.y);
				s.vertexNormal = i.normalDir;
				s.vertexTexCoord = i.uv.xy;

				s.tspace0 = i.tspace0;
				s.tspace1 = i.tspace1;
				s.tspace2 = i.tspace2;

				s.albedo=1;
				s.normal=1;
				s.gloss= _Gloss;
				s. reflectivity=1;
				s. fresnel=1;
				s. diffuseLight=0;
				s. specularLight=0;
				s. emissiveLight=0;


				LightParams l;
				l.color = _LightColor0.rgb;
				l.direction = normalize(_WorldSpaceLightPos0.xyz);
				l.attenuation = 1;
				l.shadow = 1;


				SurfaceParallaxMap(s);
				MicrosurfaceGlossMap(s);
				AlbedoMap(s);
				ReflectivitySpecularMap(s);
				DiffusionLambertianLight(s, l);
				
				DiffusionLambertianEnv(s);

				ReflectionGGXLight(s, l);
				SecReflectionGGXLight(s,l);
				ReflectionGGXEnv(s);



				half3 fincol = s.diffuseLight + s.specularLight;

				return half4(fincol,1);
	
		

			}
			ENDCG
		}
	}
}

CG部分代码:

#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"

#define mix lerp
#define vec2 half2
#define vec3 half3
#define vec4 half4
#define M_PI					3.14159265



uniform vec4 uGlossSwizzle;
uniform vec2 uGlossScaleBias;
uniform float uGlossHorizonSmooth;

sampler2D _tNormalMap, _tParallaxHeightMap, _tGlossMap, _tAlbedoMap, _tSpecularMap, _mixMap;

half _uParallaxDepthOffset, _uParallaxDepthCenter, _uGlossHorizonSmooth;
half3 _uAlbedoMapColor, _uSpecularColor, _uFresnelColor;
half _uSpecularFresnel, _uGGXSecondaryGloss, _uGGXSecondaryIntensity, _uGGXSecondaryFresnel;
float4 _Character_LightColor_c;
half _Gloss, _SpecularMapIntensity;

samplerCUBE _Cubemap;

half _EnvGloss, _CubemapIntensity, _CubemapRotation, _Bright;
//half4 _LightColor0;

struct FragmentState
{
	//inputs

	vec3 vertexEye;
    vec2 vertexTexCoord;
	vec3 vertexNormal;
	vec3 vertexTangent;
	vec3 vertexBitangent;


	//state
	vec4 albedo;
	vec3 normal;
    float gloss;
	vec3 reflectivity;
	vec3 fresnel;
	vec3 diffuseLight;
	vec3 specularLight;
	vec3 emissiveLight;

    vec3 tspace0;
    vec3 tspace1;
    vec3 tspace2;

	
};


struct LightParams
{
	vec3 color; // "colour"

	vec3 direction; // normalized vector to light

    float attenuation; // dimming (distance and other factors)

	vec4 shadow; // shadow fraction
};

void SurfaceNormalMap(inout FragmentState s)
{
	//sample and scale/bias the normal map
    vec3 nsamp = UnpackNormal(tex2D(_tNormalMap, s.vertexTexCoord));
    vec3 n =  nsamp.xyz ;
	
	//ortho-normalization
    vec3 T = s.vertexTangent;
    vec3 B = s.vertexBitangent;
    vec3 N = s.vertexNormal;



   
    n.x = dot(s.tspace0, nsamp);
    n.y = dot(s.tspace1, nsamp);
    n.z = dot(s.tspace2, nsamp);





	//store our results
    s.normal = normalize(n);
    s.vertexTangent = T;
    s.vertexBitangent = B;
    s.vertexNormal = N;
   // s.albedo.a = nsamp.a;
}


float ParallaxSample(half2 c)
{
    return 1 - (tex2D(_mixMap, c).r);
}

void SurfaceParallaxMap(inout FragmentState s)
{
    half3 dir = half3(dot(-s.vertexEye, s.vertexTangent),
					dot(-s.vertexEye, s.vertexBitangent),
					dot(-s.vertexEye, s.vertexNormal));
    half2 maxOffset = dir.xy * (_uParallaxDepthOffset / (abs(dir.z) + 0.001));

    float minSamples = 12.0;
    float maxSamples = 12.0;
    float samples = saturate(3.0 * length(maxOffset));
    float incr = rcp(lerp(minSamples, maxSamples, samples));

    half2 tc0 = s.vertexTexCoord - _uParallaxDepthCenter * maxOffset;
    float h0 = ParallaxSample(tc0);
    for (float i = incr; i <= 1.0; i += incr)
    {
        half2 tc = tc0 + maxOffset * i;
        float h1 = ParallaxSample(tc);
        if (i >= h1)
        {
						//hit! now interpolate
            float r1 = i, r0 = i - incr;
            float t = (h0 - r0) / ((h0 - r0) + (-h1 + r1));
            float r = (r0 - t * r0) + t * r1;
            s.vertexTexCoord = tc0 + r * maxOffset;
            break;
        }
        h0 = h1;
    }

				//standard normal mapping
	SurfaceNormalMap(s);
}

void MicrosurfaceGlossMap(inout FragmentState s)
{
    float g = tex2D(_mixMap, s.vertexTexCoord).g;
    s.gloss = g;
    s.gloss *= _Gloss;
    float h = saturate(dot(s.normal, s.vertexEye));
    h = _uGlossHorizonSmooth - h * _uGlossHorizonSmooth;
    s.gloss = mix(s.gloss, 1.0, h * h);
}



void AlbedoMap(inout FragmentState s)
{
    s.albedo = tex2D(_tAlbedoMap, s.vertexTexCoord);
    s.albedo.xyz *= _uAlbedoMapColor;
}

void ReflectivitySpecularMap(inout FragmentState s)
{
	float t = tex2D(_mixMap, s.vertexTexCoord).b;
    float swz = t.x;
    s.reflectivity = t.rrr;
    s.reflectivity *= _uSpecularColor * _SpecularMapIntensity;
	
    s.albedo.xyz = s.albedo.xyz - s.albedo.xyz  * s.reflectivity;
    s.fresnel = _uSpecularFresnel;
}



void ReflectionGGXLight(inout FragmentState s, LightParams l)
{
	//roughness
    float roughness = 1.0 - s.gloss;

    float a = max(roughness * roughness, 2e-3);
    float a2 = a * a;

	//light params
   // adjustAreaLightSpecular(l, reflect(-s.vertexEye, s.normal), rcp(3.141592 * a2));

	//various dot products
	vec3 H = normalize(l.direction + s.vertexEye);
    float NdotH = saturate(dot(s.normal, H));
    float VdotN = saturate(dot(s.vertexEye, s.normal));
    float LdotN = saturate(dot(l.direction, s.normal));
    float VdotH = saturate(dot(s.vertexEye, H));
	
	//horizon
    float atten = l.attenuation;
    float horizon = 1.0 - LdotN;
    horizon *= horizon;
    horizon *= horizon;
    atten = atten - atten * horizon;
	
	//incident light
	vec3 spec = l.color * l.shadow.rgb * (atten * LdotN);
	
	//microfacet distribution
    float d = (NdotH * a2 - NdotH) * NdotH + 1.0;
    d *= d;
    float D = a2 / (3.141593 * d);

	//geometric / visibility
    float k = a * 0.5;
    float G_SmithL = LdotN * (1.0 - k) + k;
    float G_SmithV = VdotN * (1.0 - k) + k;
    float G = 0.25 / (G_SmithL * G_SmithV);
	
	//fresnel
	vec3 reflectivity = s.reflectivity, fresn = s.fresnel;

	vec3 F = reflectivity + (fresn - fresn * reflectivity) * exp2((-5.55473 * VdotH - 6.98316) * VdotH);
	
    //final
    s.specularLight += D * G * F * spec;
    //(D * G) * (F * spec);
}


void SecReflectionGGXLight(inout FragmentState s, LightParams l)
{
	//roughness
    float roughness = 1.0 - s.gloss;

	roughness = saturate(roughness - roughness*_uGGXSecondaryGloss);

    float a = max(roughness * roughness, 2e-3);
    float a2 = a * a;

	//light params
   // adjustAreaLightSpecular(l, reflect(-s.vertexEye, s.normal), rcp(3.141592 * a2));

	//various dot products
	vec3 H = normalize(l.direction + s.vertexEye);
    float NdotH = saturate(dot(s.normal, H));
    float VdotN = saturate(dot(s.vertexEye, s.normal));
    float LdotN = saturate(dot(l.direction, s.normal));
    float VdotH = saturate(dot(s.vertexEye, H));
	
	//horizon
    float atten = l.attenuation;
    float horizon = 1.0 - LdotN;
    horizon *= horizon;
    horizon *= horizon;
    atten = atten - atten * horizon;
	
	//incident light
	vec3 spec = l.color * l.shadow.rgb * (atten * LdotN);
	
	//microfacet distribution
    float d = (NdotH * a2 - NdotH) * NdotH + 1.0;
    d *= d;
    float D = a2 / (3.141593 * d);

	//geometric / visibility
    float k = a * 0.5;
    float G_SmithL = LdotN * (1.0 - k) + k;
    float G_SmithV = VdotN * (1.0 - k) + k;
    float G = 0.25 / (G_SmithL * G_SmithV);
	
	//fresnel
	vec3 reflectivity = s.reflectivity, fresn = s.fresnel;

	reflectivity *= _uGGXSecondaryIntensity;
	fresn = _uGGXSecondaryFresnel;

	vec3 F = reflectivity + (fresn - fresn * reflectivity) * exp2((-5.55473 * VdotH - 6.98316) * VdotH);
	
    //final
    s.specularLight += (D * G) * (F * spec);
}




void DiffusionLambertianLight(inout FragmentState s, LightParams l)
{
  //  adjustAreaLightDiffuse(l, s.vertexPosition);
	
    float lambert = saturate(dot(s.normal, l.direction));//saturate((1.0 / 3.1415926) * dot(s.normal, l.direction));

    s.diffuseLight += (lambert * l.attenuation) *
						(l.color * l.shadow.rgb) *
						s.albedo.xyz;
}

void DiffusionLambertianEnv(inout FragmentState s)
{
 //   float3 SH = GetDirDiffuse(s.normal, _v5CharacterSHIntensity, _v5CharacterSHRotation);
    float4  SH = float4(ShadeSH9(float4(s.normal, 1.0)), 1);
    s.diffuseLight += s.albedo.xyz * SH;
}

float3 RotateAroundYInDegreesCube(float3 vertex, float degrees)
{
    half alpha = degrees * M_PI / 180.0;
    half sina, cosa;
    sincos(alpha, sina, cosa);
    half2x2 m = half2x2(cosa, -sina, sina, cosa);
    return half3(mul(m, vertex.xz), vertex.y).xzy;
}
float3 GetDirSpecularCube(float3 dir, samplerCUBE cube, float lodValue, float intensity, float rotation)
{
    float3 newDir = RotateAroundYInDegreesCube(dir, rotation);
    float3 cubeMapData = DecodeHDR(texCUBElod(_Cubemap, float4(newDir, lodValue)), 1);
				//texCUBELod(cube, float4(newDir,lodValue)).xyz * intensity;
    return cubeMapData * intensity;
}




void ReflectionGGXEnv(inout FragmentState s)
{
    float3 worldRefl = reflect(-s.vertexEye, s.normal);
    float3 spec_env_cubemap = GetDirSpecularCube(worldRefl, _Cubemap, _Gloss, _CubemapIntensity, _CubemapRotation);
    s.specularLight += spec_env_cubemap;

}

UE4材质蓝图

18f7bae714561255677fd0f5458a2666.png
eyeBall部分

221b5b2d2c93d711c3531e3d4823a0e1.png
eyeCornea部分

后续优化:目前这种做法虽然能取得不错的折射效果,但是在手机里性能消耗还是太高,Relif Map的线性步进法,步进的次数越多,效果就越好,但是这样不但增加了采样,GPU高并行的计算也被打断了,而且分为两部分模型来制作眼睛效果,从美术制作流程上也是一种负担,后续试试将两部分合成一个,利用不同的法线贴图算两遍高光,一内一外

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值