介绍

基于之前的各向异性得结果,我们可以再推广到头发渲染。Kajiya Kay头发渲染是一种经验模型。它把头发模型为一条一条细管。然后利用上一章的数学基础,但当然Kajiya Kay也加了几个细节。包括多Primary highlight, Second HighLight, Shift Tangent, 的各向异性得结果,我们可以再推广到头发渲染。Kajiya Kay头发渲染是一种经验模型。它把头发模型为一条一条细管。然后利用上一章的数学基础,但当然Kajiya Kay也加了几个细节。包括多Primary highlight, Second HighLight, Shift Tangent, 等概念。

  • 各向异性高光
  • 漫反射
  • Primary & Secondary 高光
  • Shift Tangent
  • Shift Tangent Map
  • AmbientOcclusion
  • 最终效果
  • 代码

各向异性高光

因为上一章已经说明了,高光部分 $I{s}{{anisotopic}} $ 由以下公式得到

Isanisotopic=Li∗ks∗(N⃗c⋅H⃗)n=Li∗ks∗(1−(T⃗⋅H⃗)2)nI{_s}{_{anisotopic}} = L_i * k_s * {(\vec N_c \cdot \vec H)} ^{n} = L_i * k_s * {(\sqrt{1- (\vec T \cdot \vec H)^2 } ) }^{n}Is​anisotopic​=Li​∗ks​∗(Nc​⋅H)n=Li​∗ks​∗(1−(T⋅H)2​)n

vec3 shiftTangent(vec3 T, vec3 N, float shift)
{return normalize(T + shift * N);
}float hairStrand(vec3 T, vec3 V, vec3 L, float specPower)
{vec3 H = normalize(V + L);float HdotT = dot(T, H);float sinTH = sqrt(1 - HdotT * HdotT);float dirAtten = smoothstep(-_SpecularWidth, 0, HdotT);return dirAtten * saturate(pow(sinTH, specPower)) * _SpecularScale;
}vec4 getSpecular(vec4 lightColor0,vec4 primaryColor, float primaryShift,vec4 secondaryColor, float secondaryShift,vec3 N, vec3 T, vec3 V, vec3 L, float specPower, vec2 uv)
{vec4 specular = vec4(0.0, 0.0, 0.0, 0.0);specular += primaryColor * hairStrand(T, V, L, specPower) * _SpecularScale;;return specular;
}vec4 frag(v2f i) : SV_Target
{vec3 N = normalize(i.normal);vec3 T = normalize(i.tangent);vec3 B = normalize(i.binormal);vec3 V = normalize(UnityWorldSpaceViewDir(i.pos));vec3 L = normalize(UnityWorldSpaceLightDir(i.pos));vec3 H = normalize(L + V);vec4 specular = getSpecular(_LightColor0, _PrimaryColor, _PrimaryShift, _SecondaryColor, _SecondaryShift, N, B, V, L, _specPower, i.uv);vec4 col = (specular);col.a = 1.0f;return col;
}

漫反射

Kajiya-Kay 的diffuse可以简单的使用N.L

Id=Li∗kd∗(N⃗c⋅L⃗)I{_d} = L_i * k_d * {(\vec N_c \cdot \vec L)}Id​=Li​∗kd​∗(Nc​⋅L)

vec4 getAmbientAndDiffuse(vec4 lightColor0, vec4 diffuseColor, vec3 N, vec3 L, vec2 uv)
{return (lightColor0 * diffuseColor * saturate(dot(N, L)) + vec4(0.2, 0.2, 0.2, 1.0))* tex2D(_HairTex, uv);
}

Primary & Secondary 高光

大家可以看一下下图的BaseColor,Primary 及 Secondary 高光。Primary高光是偏白的。然后Secondary 高光带点BaseColor。之后主色调Base Color。

vec4 getSpecular(vec4 lightColor0,vec4 primaryColor, float primaryShift,vec4 secondaryColor, float secondaryShift,vec3 N, vec3 T, vec3 V, vec3 L, float specPower, vec2 uv)
{vec4 specular = vec4(0.0, 0.0, 0.0, 0.0);specular += primaryColor * hairStrand(T, V, L, specPower) * _SpecularScale;;specular += secondaryColor * hairStrand(T, V, L, specPower) * _SpecularScale;return specular;
}

Shift Tangent

Kajiya-Kay模型有两个偏移切线的量,原因是改变切线头发高光也偏移。如果没有切线的偏移,我们最多只可以做到像下面没有发丝的感觉。

我们将切线沿着法线方向偏移一个数量。

Shift Tangent Map

除了一个全局的偏移值,我们也可以控制每像素的偏移。我们叫这个map叫Shift Tangent Map

Ambient Occlusion

最后,我们可以加上AO,这个AO的数值可以从vertex color或AO贴图得到。这个是用来模拟Base Color Shadowed。

vec4 getSpecular(vec4 lightColor0,vec4 primaryColor, float primaryShift,vec4 secondaryColor, float secondaryShift,vec3 N, vec3 T, vec3 V, vec3 L, float specPower, vec2 uv, float ambientOcc)
{vec4 specular = vec4(0.0, 0.0, 0.0, 0.0);specular += primaryColor * hairStrand(T, V, L, specPower) * _SpecularScale;;specular += secondaryColor * hairStrand(T, V, L, specPower) * _SpecularScale;specular *= ambientOcc;return specular ;
}vec4 getAmbientAndDiffuse(vec4 lightColor0, vec4 diffuseColor, vec3 N, vec3 L, vec2 uv, float ambientOcc)
{float ambientDiffuse = (lightColor0 * diffuseColor * saturate(dot(N, L)) + vec4(0.2, 0.2, 0.2, 1.0))* tex2D(_HairTex, uv) * ambientOcc;return ambientDiffuse * ambientOcc;
}

最终效果

代码

vec3 shiftTangent(vec3 T, vec3 N, float shift)
{return normalize(T + shift * N);
}float hairStrand(vec3 T, vec3 V, vec3 L, float specPower)
{vec3 H = normalize(V + L);float HdotT = dot(T, H);float sinTH = sqrt(1 - HdotT * HdotT);float dirAtten = smoothstep(-_SpecularWidth, 0, HdotT);return dirAtten * saturate(pow(sinTH, specPower)) * _SpecularScale;
}vec4 getAmbientAndDiffuse(vec4 lightColor0, vec4 diffuseColor, vec3 N, vec3 L, vec2 uv)
{return (lightColor0 * diffuseColor * saturate(dot(N, L)) + vec4(0.2, 0.2, 0.2, 1.0))* tex2D(_HairTex, uv);
}vec4 getSpecular(vec4 lightColor0,vec4 primaryColor, float primaryShift,vec4 secondaryColor, float secondaryShift,vec3 N, vec3 T, vec3 V, vec3 L, float specPower, vec2 uv)
{float shiftTex = tex2D(_SpecularShift, uv) - 0.5;vec3 t1 = shiftTangent(T, N, primaryShift + shiftTex);vec3 t2 = shiftTangent(T, N, secondaryShift + shiftTex);vec4 specular = vec4(0.0, 0.0, 0.0, 0.0);specular += primaryColor * hairStrand(t1, V, L, specPower) * _SpecularScale;;specular += secondaryColor * hairStrand(t2, V, L, specPower) * _SpecularScale;return specular;
}vec4 frag(v2f i) : SV_Target
{vec3 N = normalize(i.normal);vec3 T = normalize(i.tangent);vec3 B = normalize(i.binormal);vec3 V = normalize(UnityWorldSpaceViewDir(i.pos));vec3 L = normalize(UnityWorldSpaceLightDir(i.pos));vec3 H = normalize(L + V);vec4 ambientdiffuse = getAmbientAndDiffuse(_LightColor0, _DiffuseColor, N, L, i.uv);vec4 specular = getSpecular(_LightColor0, _PrimaryColor, _PrimaryShift, _SecondaryColor, _SecondaryShift, N, B, V, L, _specPower, i.uv);vec4 col = (ambientdiffuse + specular);col.a = 1.0f;return col;
}

代码Unity

Shader "Mac/HairShader2"
{Properties{_HairTex("Texture", 2D) = "white" {}_SpecularShift("Hair Shifted Texture", 2D) = "white" {}_DiffuseColor("DiffuseColor", Color) = (0.0, 0.0, 0.0, 0.0)_PrimaryColor("Specular1Color", Color) = (0.0, 0.0, 0.0, 0.0)_PrimaryShift("PrimaryShift", Range(-4, 4)) = 0.0_SecondaryColor("Specular2Color", Color) = (0.0, 0.0, 0.0, 0.0)_SecondaryShift("SecondaryShift", Range(-4, 4)) = 0.5_specPower("SpecularPower", Range(0, 50)) = 20_SpecularWidth("SpecularWidth", Range(0, 1)) = 0.5_SpecularScale("SpecularScale", Range(0, 1)) = 0.3}SubShader{Tags { "RenderType"="Transparent" }LOD 100Pass{Blend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal: NORMAL;float4 tangent: TANGENT;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;float3 tangent : TEXCOORD1;float3 normal : TEXCOORD2;float3 binormal: TEXCOORD3;float3 pos : TEXCOORD4;UNITY_FOG_COORDS(1)};sampler2D _HairTex;float4 _HairTex_ST;sampler2D _SpecularShift;float4 _SpecularShift_ST;float4 _DiffuseColor;float4 _PrimaryColor;float _PrimaryShift;float4 _SecondaryColor;float _SecondaryShift;float _specPower;float _SpecularWidth;float _SpecularScale;v2f vert (appdata v){v2f o;UNITY_INITIALIZE_OUTPUT(v2f, o);o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _HairTex);o.normal = UnityObjectToWorldNormal(v.normal);o.tangent = UnityObjectToWorldDir(v.tangent);o.binormal = cross(v.normal, v.tangent) * v.tangent.w * unity_WorldTransformParams.w;o.pos = mul(unity_ObjectToWorld, v.vertex);UNITY_TRANSFER_FOG(o,o.vertex);return o;}fixed3 shiftTangent(fixed3 T, fixed3 N, fixed shift){return normalize(T + shift * N);}fixed hairStrand(fixed3 T, fixed3 V, fixed3 L, fixed specPower){fixed3 H = normalize(V + L);fixed HdotT = dot(T, H);fixed sinTH = sqrt(1 - HdotT * HdotT);fixed dirAtten = smoothstep(-_SpecularWidth, 0, HdotT);return dirAtten * saturate(pow(sinTH, specPower)) * _SpecularScale;}fixed4 getAmbientAndDiffuse(fixed4 lightColor0, fixed4 diffuseColor, fixed3 N, fixed3 L, fixed2 uv){return (lightColor0 * diffuseColor * saturate(dot(N, L)) + fixed4(0.2, 0.2, 0.2, 1.0)) * tex2D(_HairTex, uv);}fixed4 getSpecular(fixed4 lightColor0, fixed4 primaryColor, fixed primaryShift,fixed4 secondaryColor, fixed secondaryShift,fixed3 N, fixed3 T, fixed3 V, fixed3 L, fixed specPower, fixed2 uv){float shiftTex = tex2D(_SpecularShift, uv) - 0.5;fixed3 t1 = shiftTangent(T, N, primaryShift + shiftTex);fixed3 t2 = shiftTangent(T, N, secondaryShift + shiftTex);fixed4 specular = fixed4(0.0, 0.0, 0.0, 0.0);specular += primaryColor * hairStrand(t1, V, L, specPower) * _SpecularScale;;specular += secondaryColor * hairStrand(t2, V, L, specPower) * _SpecularScale;return specular;}fixed4 frag (v2f i) : SV_Target{fixed3 N = normalize(i.normal);fixed3 T = normalize(i.tangent);fixed3 B = normalize(i.binormal);fixed3 V = normalize(UnityWorldSpaceViewDir(i.pos));fixed3 L = normalize(UnityWorldSpaceLightDir(i.pos));fixed3 H = normalize(L + V);fixed4 ambientdiffuse = getAmbientAndDiffuse(_LightColor0, _DiffuseColor, N, L, i.uv);fixed4 specular = getSpecular(_LightColor0, _PrimaryColor, _PrimaryShift, _SecondaryColor, _SecondaryShift, N, B, V, L, _specPower, i.uv);fixed4 col = (ambientdiffuse + specular);col.a = 1.0f;UNITY_APPLY_FOG(i.fogCoord, col);return col;}ENDCG}}
}

各向异性渲染(二)Kajiya Kay头发渲染相关推荐

  1. 图形学基础|各项异性与头发渲染

    图形学基础|各项异性与头发渲染 文章目录 图形学基础|各项异性与头发渲染 一.前言 二.各向异性光照 2.1 各向异性光照现象 2.2 ShadingModel扩展 三.头发光照模型 3.1 Kaji ...

  2. PBR渲染(四)——PBR头发渲染

    PBR头发渲染 1.渲染方案 首先在光照模型的选择上,选择了Marschner基于物理的理论模型. 理论模型:头发不是完美的圆柱体或管子,实际上,头发更像是一系列堆叠的圆锥体.每一根头发纤维由角质层( ...

  3. Android 颜色渲染(二) 颜色区域划分原理与实现思路

    在前面的系列我已经将Android中颜色渲染的原理及使用做了一个整体上概述. 现在开始根据一个比较复杂的实现进行具体的分析,这就是PhotoShop中的调色板应用 首先还是看一下最终的实现效果:    ...

  4. 【微信小程序】二维数组列表渲染

    微信小程序官方文档主要是一维数组列表渲染的案例: Page({items: [{message: 'foo',},{message: 'bar'}] }) <view wx:for=" ...

  5. FFmpeg学习之二 (yuv视频渲染)

    FFmpeg学习之二 (yuv视频渲染) yuv简介 1.yuv是什么 2.yuv采集方式 3.yuv存储方式 4.yuv格式 yuv视频渲染 1. iOS YUV视频渲染 1.1 IOS利用open ...

  6. Unity渲染(二):Shader着色器基础入门之渲染Image图片

    Unity渲染(二):图片渲染 通过这里,你会学习到怎么将一张图片渲染到UI的Image组件或者SpriteRenderer上,以及透明物体的渲染. 上一章:Unity渲染(一):着色器基础入门之纯色 ...

  7. 16 React【无人点餐无人收银系统案例】路由配置、菜品列表制作、请求数据渲染二维数组、动态路由传值 【基础项目】

    一.页面 二.react-router4.x 路由配置 App.js import React from "react";import { BrowserRouter as Rou ...

  8. 基于C#的AE二次开发-地图渲染之分级设色符号化

    基于C#的AE二次开发-地图渲染之分级设色渲染符号化 我的开发环境为ArcGIS Engine 10.2与Visual studio2010.主地图名称为axMapControl1,Toc目录名为ax ...

  9. 游戏渲染技术:前向渲染 vs 延迟渲染 vs Forward+渲染(二)

    GTA5 2 前向渲染 前向渲染是三个光照技术中最简单的,也是游戏图形渲染中最常见的技术.出于这个原因,也是光照计算最昂贵的技术,它不允许在场景中出现大量的动态光源. 大部分使用前向渲染的图形引擎会采 ...

最新文章

  1. 网络推广营销浅析网站度过“沙盒期”后,为什么还不收录?
  2. java合集框架第一天
  3. MySQL 优化之 index_merge (索引合并)
  4. Android USB转串口通信开发基本流程
  5. K8S仪表板Service unavailable故障的解决办法
  6. 网工视频13第13章.计算机系统开发运行与配置疑难问题
  7. xamarin_如何实现声明性Xamarin表单验证
  8. roipool and roialign difference
  9. Linux安装后的基本配置
  10. Flask:an ImportError was raised问题解决
  11. 哪几种情况可以销毁session(一般有3种)
  12. java hostwrite_Java IOUtils.write方法代碼示例
  13. 蓝星实物微商城H5源码 附搭建教程
  14. 1-2 Verilog 4位 二选一 多路选择器
  15. android麦克风被禁用怎么办,为什么微信麦克风被禁用?如何开启?
  16. 抖音壁纸小程序,星光壁纸小程序2.0版本,升级版
  17. 极客日报:苹果iPhone系列成交额5秒破亿;荣耀回应:没有安卓授权是假消息;魅族宣布将接入HarmonyOS
  18. web前端项目实战_vue项目仿美团【爱创课堂】
  19. flask部署阿里云服务器,公网ip访问不了(一些问题及解答)
  20. 片上网络路由算法综述

热门文章

  1. wget 正在连接 127.0.0.1:36627... 失败:拒绝连接
  2. 数据批处理神器-Spring Batch(1)简介及使用场景
  3. 前端css高频使用的属性总结
  4. cnpm不是内部命令
  5. cnpm命令安装,不是内部命令解决方案。
  6. EDM邮件营销内容策略技巧
  7. JAVA笔记(2)变量初始化,数组,包,继承,修饰符
  8. 苹果换手机怎么转移数据?苹果、安卓都可以一键转移数据
  9. Android 总结:Manifest文件中,application和activity标签属性详解
  10. 【里程碑】| 数据应用开发管理框架DataSphere Studio 1.1.0 新版本发布