Cg Programming/Unity/Specular Highlights at Silhouettes轮廓处的镜面高光

本文主要是介绍Cg Programming/Unity/Specular Highlights at Silhouettes轮廓处的镜面高光,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本教程镜面高光的菲涅耳系数。

这是关于光照教程中的其中一个教程,这个光照超出了Phone反射模型的范围。但是,它是基于章节“镜面高光”(逐顶点光照)以及章节“光滑镜面高光”(逐像素光照)中描述的Phone反射模型光照。如果你没阅读过那两章,建议先阅读一下。

这里写图片描述

当光线掠过表面时,大多数材质(比如亚光纸)会表现出强烈的镜面反射;如上图所示,背光从观察者相反方向进行反射。对一些材质来说菲涅耳系数解释了这种强反射。当然,还有其它原因会导致明亮的轮廓,比如半透明的头发或织物(参考章节“半透明表面”)。

有趣地是,这种效果通过很难被看到,因为很可能轮廓的背景非常明亮。但是,在这种情况下,一个明亮的轮廓会混合进背景中,于是会变得很难被看到。

译者注:总感觉文章说得不是很明白,不知道是不是自己翻译的问题。

所谓菲涅耳反射,其实就是根据视角方向来控制反射程度。当光照射到物体表面时,被反射的光和入射的光之间有一定的比率关系,而这种关系就是用菲涅耳等式计算的。

菲涅耳系数的Schlick近似

这里写图片描述
菲涅耳系数这里写图片描述描述了波长为λ的非偏振光照下非导电材质的镜面反射。Schlick的近似等式如下:
这里写图片描述
这里V是指向观察者的归一化方向,H是归一化的中间向量: H = (V + L) / |V + L|,L是指向光源的归一化方向。H·V = 1时这里写图片描述就是反射率,也就是指向光源的方向、指向观察者的方向以及中间向量都是等价的。当中间向量垂直于指向观察者的方向V,也就意味着指向光源的方向跟指向观察者的方向是相反的(即掠射光反射的情况)。实际上在这种情况下这里写图片描述是独立于波长的,并且材质表现得就像一面完美的镜子。

使用内置Cg函数lerp(x,y,w) = x*(1-w) + y*w,你可以改写Schlick的近似函数:
这里写图片描述
至少在一些GPU上面,这个会稍微更有效一点。我们将会通过允许每个颜色分量有不同的值这里写图片描述把波长的依赖性考虑进来,也就是我们认为它是一个RGB向量。实际上,我们把它认同于章节“镜面高光”中的这里写图片描述。菲涅耳系数会在指向观察者的方向和中间向量之间的角度上添加材质颜色这里写图片描述的独立性。于是,我们会在任何镜面反射运算中把常量材质颜色这里写图片描述替换为Schlick的近似等式(使用这里写图片描述)。

举例来说,在Phong反射模型中我们对于镜面项的等式是:
这里写图片描述
这里写图片描述替换为有这里写图片描述的菲涅耳系数的Schlick近似:
这里写图片描述

代码实现

该实现是基于章节“光滑镜面高光”中的着色器代码。这只是计算中间向量并且包含了菲涅耳系数的近似:

            float3 specularReflection;if (dot(normalDirection, lightDirection) < 0.0) // light source on the wrong side?{specularReflection = float3(0.0, 0.0, 0.0); // no specular reflection}else // light source on the right side{float3 halfwayDirection = normalize(lightDirection + viewDirection);float w = pow(1.0 - max(0.0, dot(halfwayDirection, viewDirection)), 5.0);specularReflection = attenuation * _LightColor0.rgb * lerp(_SpecColor.rgb, float3(1.0, 1.0, 1.0), w) * pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)), _Shininess);}

完整的着色器代码

Shader "Cg Fresnel highlights" {Properties {_Color ("Diffuse Material Color", Color) = (1,1,1,1) _SpecColor ("Specular Material Color", Color) = (1,1,1,1) _Shininess ("Shininess", Float) = 10}SubShader {Pass {    Tags { "LightMode" = "ForwardBase" } // pass for ambient light and first light sourceCGPROGRAM#pragma vertex vert  #pragma fragment frag #include "UnityCG.cginc"uniform float4 _LightColor0; // color of light source (from "Lighting.cginc")// User-specified propertiesuniform float4 _Color; uniform float4 _SpecColor; uniform float _Shininess;struct vertexInput {float4 vertex : POSITION;float3 normal : NORMAL;};struct vertexOutput {float4 pos : SV_POSITION;float4 posWorld : TEXCOORD0;float3 normalDir : TEXCOORD1;};vertexOutput vert(vertexInput input) {vertexOutput output;float4x4 modelMatrix = _Object2World;float3x3 modelMatrixInverse = _World2Object;output.posWorld = mul(modelMatrix, input.vertex);output.normalDir = normalize(mul(input.normal, modelMatrixInverse));output.pos = mul(UNITY_MATRIX_MVP, input.vertex);return output;}float4 frag(vertexOutput input) : COLOR{float3 normalDirection = normalize(input.normalDir);float3 viewDirection = normalize(_WorldSpaceCameraPos - input.posWorld.xyz);float3 lightDirection;float attenuation;if (0.0 == _WorldSpaceLightPos0.w) // directional light?{attenuation = 1.0; // no attenuationlightDirection = normalize(_WorldSpaceLightPos0.xyz);} else // point or spot light{float3 vertexToLightSource = _WorldSpaceLightPos0.xyz - input.posWorld.xyz;float distance = length(vertexToLightSource);attenuation = 1.0 / distance; // linear attenuation lightDirection = normalize(vertexToLightSource);}float3 ambientLighting = UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;float3 diffuseReflection = attenuation * _LightColor0.rgb * _Color.rgb* max(0.0, dot(normalDirection, lightDirection));float3 specularReflection;if (dot(normalDirection, lightDirection) < 0.0) // light source on the wrong side?{specularReflection = float3(0.0, 0.0, 0.0); // no specular reflection}else // light source on the right side{float3 halfwayDirection = normalize(lightDirection + viewDirection);float w = pow(1.0 - max(0.0, dot(halfwayDirection, viewDirection)), 5.0);specularReflection = attenuation * _LightColor0.rgb * lerp(_SpecColor.rgb, float3(1.0, 1.0, 1.0), w) * pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)), _Shininess);}return float4(ambientLighting + diffuseReflection + specularReflection, 1.0);}ENDCG}Pass {    Tags { "LightMode" = "ForwardAdd" } // pass for additional light sourcesBlend One One // additive blendingCGPROGRAM#pragma vertex vert  #pragma fragment frag #include "UnityCG.cginc"uniform float4 _LightColor0; // color of light source (from "Lighting.cginc")// User-specified propertiesuniform float4 _Color; uniform float4 _SpecColor; uniform float _Shininess;struct vertexInput {float4 vertex : POSITION;float3 normal : NORMAL;};struct vertexOutput {float4 pos : SV_POSITION;float4 posWorld : TEXCOORD0;float3 normalDir : TEXCOORD1;};vertexOutput vert(vertexInput input) {vertexOutput output;float4x4 modelMatrix = _Object2World;float4x4 modelMatrixInverse = _World2Object;output.posWorld = mul(modelMatrix, input.vertex);output.normalDir = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);output.pos = mul(UNITY_MATRIX_MVP, input.vertex);return output;}float4 frag(vertexOutput input) : COLOR{float3 normalDirection = normalize(input.normalDir);float3 viewDirection = normalize(_WorldSpaceCameraPos - input.posWorld.xyz);float3 lightDirection;float attenuation;if (0.0 == _WorldSpaceLightPos0.w) // directional light?{attenuation = 1.0; // no attenuationlightDirection = normalize(_WorldSpaceLightPos0.xyz);} else // point or spot light{float3 vertexToLightSource = _WorldSpaceLightPos0.xyz - input.posWorld.xyz;float distance = length(vertexToLightSource);attenuation = 1.0 / distance; // linear attenuation lightDirection = normalize(vertexToLightSource);}float3 diffuseReflection = attenuation * _LightColor0.rgb * _Color.rgb* max(0.0, dot(normalDirection, lightDirection));float3 specularReflection;if (dot(normalDirection, lightDirection) < 0.0) // light source on the wrong side?{specularReflection = float3(0.0, 0.0, 0.0); // no specular reflection}else // light source on the right side{float3 halfwayDirection = normalize(lightDirection + viewDirection);float w = pow(1.0 - max(0.0, dot(halfwayDirection, viewDirection)), 5.0);specularReflection = attenuation * _LightColor0.rgb * lerp(_SpecColor.rgb, float3(1.0, 1.0, 1.0), w) * pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)), _Shininess);} return float4(diffuseReflection + specularReflection, 1.0);}ENDCG}}Fallback "Specular"
}

艺术控制

对以上的实现,一个有意义的修改就是把power 5.0替换为用户指定的着色器属性。这将使艺术家可以根据他们的艺术需求来选择夸大或减弱菲涅耳系数的效果。

半透明表面的影响

菲涅耳系数除了会影响镜面高光,它还会影响不透明表面的不透明度α。实际上,菲涅耳系数描述了在掠射光照下的表面如何变得反射更强,这意味着少数光是被吸收、折射或是透射的,即透明度T减小,同时不透明度α = 1 - T增大。最终,菲涅耳系数可以用表面法向量N代替半矢量H来计算,以及使用以下等式,不透明表面的不透明度会从用户指定的值这里写图片描述(在表面法线方向上观察)增加到1:
这里写图片描述

在章节“轮廓增强”中,不透明度被认为是由于光线穿过半透明材质层而衰减的结果。这种不透明度应该由于以下方式增加反射率的不透明度结合起来(原文是:This opacity should be combined with the opacity due to increased reflectivity in the following way。这个怎么翻译!!!)。总的不透明度这里写图片描述就是1减去总的透明度这里写图片描述,它是根据衰减得到的透明度这里写图片描述(1-这里写图片描述)和根据菲涅耳系数得到的透明度这里写图片描述(1-这里写图片描述)相乘得到的。
这里写图片描述

这里写图片描述就是上面计算得到的不透明度,而这里写图片描述是章节“轮廓增强”中计算得到的不透明度。对于平行于表面法向量的观察方向来说,这里写图片描述这里写图片描述可以被用户指定。然后对于法向量来说,等式可以修正这里写图片描述,并且实际上它会修正所有常量,因此就可以为所有视图方向计算这里写图片描述。注意不管漫反射还是镜面反射都不应该乘以不透明度这里写图片描述,因为镜面反射已经乘以了菲涅耳系数,并且由于衰减这里写图片描述漫反射应该只是乘以不透明度。

总结

恭喜,你完成了一些比较高级的教程中的一章!我们看到了:

  • 什么是菲涅耳系数。
  • 什么是对菲涅耳系数的Schlick近似。
  • 对于镜面高光如何实现Schlick近似。
  • 如何对该实现添加更多的艺术控制。
  • 对于半透明表面如何使用菲涅耳系数。

这篇关于Cg Programming/Unity/Specular Highlights at Silhouettes轮廓处的镜面高光的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/797356

相关文章

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

C#和Unity中的中介者模式使用方式

《C#和Unity中的中介者模式使用方式》中介者模式通过中介者封装对象交互,降低耦合度,集中控制逻辑,适用于复杂系统组件交互场景,C#中可用事件、委托或MediatR实现,提升可维护性与灵活性... 目录C#中的中介者模式详解一、中介者模式的基本概念1. 定义2. 组成要素3. 模式结构二、中介者模式的特点

使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)

《使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)》字体设计和矢量图形处理是编程中一个有趣且实用的领域,通过Python的matplotlib库,我们可以轻松将字体轮廓... 目录背景知识字体轮廓的表示实现步骤1. 安装依赖库2. 准备数据3. 解析路径指令4. 绘制图形关键

Unity Post Process Unity后处理学习日志

Unity Post Process Unity后处理学习日志 在现代游戏开发中,后处理(Post Processing)技术已经成为提升游戏画面质量的关键工具。Unity的后处理栈(Post Processing Stack)是一个强大的插件,它允许开发者为游戏场景添加各种视觉效果,如景深、色彩校正、辉光、模糊等。这些效果不仅能够增强游戏的视觉吸引力,还能帮助传达特定的情感和氛围。 文档

Unity协程搭配队列开发Tips弹窗模块

概述 在Unity游戏开发过程中,提示系统是提升用户体验的重要组成部分。一个设计良好的提示窗口不仅能及时传达信息给玩家,还应当做到不干扰游戏流程。本文将探讨如何使用Unity的协程(Coroutine)配合队列(Queue)数据结构来构建一个高效且可扩展的Tips弹窗模块。 技术模块介绍 1. Unity协程(Coroutines) 协程是Unity中的一种特殊函数类型,允许异步操作的实现

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光 一,前言二,资源包内容三,免费获取资源包 一,前言 在创意的世界里,每一个细节都能决定一个项目的独特魅力。今天,要向大家介绍一款令人惊艳的粒子效果包 ——Super Confetti FX。 二,资源包内容 💥充满活力与动态,是 Super Confetti FX 最显著的标签。它宛如一位

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(4)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)-CSDN博客  这节就是真正的存储数据了   理清一下思路: 1.存储路径并检查 //2进制文件类存储private static string Data_Binary_Pa

Unity Adressables 使用说明(一)概述

使用 Adressables 组织管理 Asset Addressables 包基于 Unity 的 AssetBundles 系统,并提供了一个用户界面来管理您的 AssetBundles。当您使一个资源可寻址(Addressable)时,您可以使用该资源的地址从任何地方加载它。无论资源是在本地应用程序中可用还是存储在远程内容分发网络上,Addressable 系统都会定位并返回该资源。 您

Unity Adressables 使用说明(六)加载(Load) Addressable Assets

【概述】Load Addressable Assets Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一: Address:包含你分配给资源的地址的字符串。Label:包含分配给一个或多个资源的标签的字符串。AssetReference Obj

在Unity环境中使用UTF-8编码

为什么要讨论这个问题         为了避免乱码和更好的跨平台         我刚开始开发时是使用VS开发,Unity自身默认使用UTF-8 without BOM格式,但是在Unity中创建一个脚本,使用VS打开,VS自身默认使用GB2312(它应该是对应了你电脑的window版本默认选取了国标编码,或者是因为一些其他的原因)读取脚本,默认是看不到在VS中的编码格式,下面我介绍一种简单快