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 Shader】片段着色器(Fragment Shader)的概念及其使用方法

在Unity和图形编程中,片段着色器(Fragment Shader)是渲染管线中的一个阶段,负责计算屏幕上每个像素(片段)的颜色和特性。片段着色器通常在顶点着色器和任何几何处理之后运行,是决定最终像素颜色的关键步骤。 Fragment Shader的概念: 像素处理:片段着色器处理经过顶点着色器和几何着色器处理后,映射到屏幕空间的像素。颜色计算:它计算每个像素的颜色值,这可能包括纹理采样、光

【Unity Shader】Alpha Blend(Alpha混合)的概念及其使用示例

在Unity和图形编程中,Alpha Blend(也称为Alpha混合)是一种用于处理像素透明度的技术。它允许像素与背景像素融合,从而实现透明或半透明的效果。Alpha Blend在渲染具有透明度的物体(如窗户、玻璃、水、雾等)时非常重要。 Alpha Blend的概念: Alpha值:Alpha值是一个介于0(完全透明)和1(完全不透明)的数值,用于表示像素的透明度。混合模式:Alpha B

Apple - Media Playback Programming Guide

本文翻译整理自:Media Playback Programming Guide(Updated: 2018-01-16 https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/MediaPlaybackGuide/Contents/Resources/en.lproj/Introduction

基于深度学习的轮廓检测

基于深度学习的轮廓检测 轮廓检测是计算机视觉中的一项关键任务,旨在识别图像中物体的边界或轮廓。传统的轮廓检测方法如Canny边缘检测和Sobel算子依赖于梯度计算和阈值分割。而基于深度学习的方法通过训练神经网络来自动学习图像中的轮廓特征,能够在复杂背景和噪声条件下实现更精确和鲁棒的检测效果。 深度学习在轮廓检测中的优势 自动特征提取:深度学习模型能够自动从数据中学习多层次的特征表示,而不需要

Unity Meta Quest 开发:关闭 MR 应用的安全边界

社区链接: SpatialXR社区:完整课程、项目下载、项目孵化宣发、答疑、投融资、专属圈子 📕教程说明 这期教程我将介绍如何在应用中关闭 Quest 系统的安全边界。 视频讲解: https://www.bilibili.com/video/BV1Gm42157Zi 在 Unity 中导入 Meta XR SDK,进行环境配置后,打开 Assets > Plugins > An

Unity 字体创建时候容易导致字体文件不正确的一种情况

上面得到了两种字体格式,一种是TextMeshPro的,另一种是Unity UI系统中默认使用的字体资源。其原因是创建的位置不同导致的。 1.下面是TextMeshPro字体创建的位置 2:下面是Unity UI系统中默认使用的字体资源

摄像头画面显示于unity场景

🐾 个人主页 🐾 🪧阿松爱睡觉,横竖醒不来 🏅你可以不屠龙,但不能不磨剑🗡 目录 一、前言二、UI画面三、显示于场景四、结语 一、前言 由于标题限制,这篇文章主要是讲在unity中调用摄像头,然后将摄像头捕捉到的画面显示到场景中,无论是UI画面还是场景中的某个物体上;至于应用的场景可以用于AR增强现实。 那么话不多说,直接开始今

unity开发 --------- NGUI (UITable)

unity开发 --------- NGUI UITable与UIGrid相似,都是实现自动排序的。但UIGrid的元素大小是由我们来指定的,而Table中的元素的大小是根据元素本身计算出来的。 UITable还保存了元素的顺序List<Transform>。每次重排序,都会更新此List。除了要计算元素的Bound和保存List外,其他基本与UIGrid一致。 unity开发 ---

unity开发 --------- NGUI (UIGrid)

unity开发 --------- NGUI  UIGrid可以实现多个gameobject自动排序。可以设定其排序方向、每个元素的宽度,高度等。 public Arrangement arrangement = Arrangement.Horizontal;public int maxPerLine = 0;public float cellWi

unity开发 --------- NGUI(Localization、UILocalize)

unity开发 --------- NGUI NGUI支持动态加载资源功能。比如语言选择:假如当前语言为中文,当将语言更改为英文时,所有UI上的文字也立即变成了英文。此功能是用Localization和UILocalize两个脚本配合完成的。 Localization中记录多种配置方案,当更改配置方案时,由Localization发送通知,通知各UILocalize更新。 NGU