Cg Programming/Unity/Brushed Metal拉丝金属

2024-03-11 09:08

本文主要是介绍Cg Programming/Unity/Brushed Metal拉丝金属,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本教程涵盖了各向异性镜面高光

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

这里写图片描述
对于纸张、塑料以及一些其它各向同性的材质来说,Phone反射模型看上去还是相当不错的。本教程特别关注各向异性反射材质(即非圆形高光),就比如上图中的拉丝铝。

Ward的各向异性反射模型

Gregory Ward在他的论文“各向异性反射测量与建模”,计算机图形学(SIGGRAPH ’92 Proceedings), pp. 265–272, July 1992中公布了一种合适的各向异性反射模型。(网上可以找到该论文。)这个模型用BRDF(双向反射分布函数)来描述反射,它是一个四维函数,描述了任意方向的光线是如何反射到任意其它方向上。他的BRDF模型包含了两项:一个漫反射项这里写图片描述,以及一个比较复杂的镜面反射项。

让我们先来看一下漫射项这里写图片描述:π是一个常量(约是3.14159),这里写图片描述指定了漫反射率。原则上每个波长的反射率是必要的;但是,通常对三种颜色分量(红、绿和蓝)中的每一个都指定一个反射率。如果我们包含了常量π,这里写图片描述就表示漫射材质颜色这里写图片描述,我们第一次看到它是在章节“漫反射”中,但是它也出现在Phone反射模型中(参考章节“镜面高光”)。你可能疑惑为什么因子max(0, L·N)没有出现在BRDF中。原因在于BRDF就是如此定义的,这个因子没有包含进来(因为它实际并不是材质的一个属性),但是当计算任何光照的时候应该把它乘以BRDF。

于是,为了对不透明材质实现一个指定的BRDF,我们必须用max(0, L·N)乘以BRDF所有的项,以及除非我们想要实现物理上正确的光照,我们可以把任何常量因子替换成用户指定的颜色,它通常比物理量更容易控制。

对于BRDF模型中特殊的项,Ward在他的论文中提出了一个近似方程5b。我稍微修改了一下,这样就可以使用归一化的表面法向量N,指向观察者的归一化向量V,指向光源的归一化向量L,以及归一化的中间向量H(V + L) / | V + L |。利用这些向量,Ward对于这个特殊项的近似就成为了这个样子:
这里写图片描述
这里,这里写图片描述是镜面反射率,它描述了颜色和镜面高光的强度;这里写图片描述这里写图片描述是描述高光形状和大小的材质常量。既然所有这些变量就是材质常量,我们可以把它们整合到一个常量这里写图片描述中去。于是我们得到一个稍微简短点的版本:
这里写图片描述

记住当在着色器中实现它时,我们仍然需要把BRDF项乘以L·N,并且当L·N小于0的时候把它设置为0。此外,如果L·N的值小于0它也应该是0,也就是如果从“错”的面看表面的话。

还有两个向量没有描述过:T和B。T是表面上刷(拉丝)的方向,B垂直于T并且也在表面上。Unity为我们提供了一个表面上的切线向量作为一个顶点属性(参考章节“着色器调试”),我们就把它用作向量T。计算N和T的叉乘可以得到向量B,它垂直于N和T。

Ward的BRDF模型的实现

我们的实现基于章节“光滑镜面高光”中的逐像素光照着色器。对于切线向量T(也就是刷的方向)我们需要另一个顶点输出参数tangentDir,并且我们也需要在顶点着色器中计算viewDir这样可以在片元着色器中节省一些指令。在片元着色器中,我们计算额外两个方向:中间向量H的halfwayVector和副法线向量B的binormalDirection。属性_Color就是这里写图片描述_SpecColor就是这里写图片描述_AlphaX就是这里写图片描述_AlphaY就是这里写图片描述

这个片元着色器跟章节“光滑镜面高光”中的版本非常类似,除了它会计算halfwayVectorbinormalDirection,为镜面部分实现一个不同的等式。此外,这个着色器只计算一次点乘L·N并且把值存储在dotLN中,这样就可以重复使用而无需再计算了。

float4 frag(vertexOutput input) : COLOR{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 halfwayVector = normalize(lightDirection + input.viewDir);float3 binormalDirection = cross(input.normalDir, input.tangentDir);float dotLN = dot(lightDirection, input.normalDir); // compute this dot product only oncefloat3 ambientLighting = UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;float3 diffuseReflection = attenuation * _LightColor0.rgb * _Color.rgb * max(0.0, dotLN);float3 specularReflection;if (dotLN < 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{float dotHN = dot(halfwayVector, input.normalDir);float dotVN = dot(input.viewDir, input.normalDir);float dotHTAlphaX = dot(halfwayVector, input.tangentDir) / _AlphaX;float dotHBAlphaY = dot(halfwayVector, binormalDirection) / _AlphaY;specularReflection = attenuation * _LightColor0.rgb * _SpecColor.rgb * sqrt(max(0.0, dotLN / dotVN)) * exp(-2.0 * (dotHTAlphaX * dotHTAlphaX + dotHBAlphaY * dotHBAlphaY) / (1.0 + dotHN));}return float4(ambientLighting + diffuseReflection + specularReflection, 1.0);}

注意sqrt(max(0, dotLN / dotVN))这项,它由这里写图片描述乘以(L·N)得到。这个保证了结果始终大于0。

完整的着色器代码只是定义了合适的属性并且为切线添加了另一个顶点输入参数。它也需要第二个通道,里面有加性混合但没有额外光源的环境光照。

Shader "Cg anisotropic per-pixel lighting" {Properties {_Color ("Diffuse Material Color", Color) = (1,1,1,1) _SpecColor ("Specular Material Color", Color) = (1,1,1,1) _AlphaX ("Roughness in Brush Direction", Float) = 1.0_AlphaY ("Roughness orthogonal to Brush Direction", Float) = 1.0}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 _AlphaX;uniform float _AlphaY;struct vertexInput {float4 vertex : POSITION;float3 normal : NORMAL;float4 tangent : TANGENT;};struct vertexOutput {float4 pos : SV_POSITION;float4 posWorld : TEXCOORD0;// position of the vertex (and fragment) in world space float3 viewDir : TEXCOORD1;// view direction in world spacefloat3 normalDir : TEXCOORD2;// surface normal vector in world spacefloat3 tangentDir : TEXCOORD3;// brush direction in world space};vertexOutput vert(vertexInput input) {vertexOutput output;float4x4 modelMatrix = _Object2World;float4x4 modelMatrixInverse = _World2Object; output.posWorld = mul(modelMatrix, input.vertex);output.viewDir = normalize(_WorldSpaceCameraPos - output.posWorld.xyz);output.normalDir = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);output.tangentDir = normalize(mul(modelMatrix, float4(input.tangent.xyz, 0.0)).xyz);output.pos = mul(UNITY_MATRIX_MVP, input.vertex);return output;}float4 frag(vertexOutput input) : COLOR{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 halfwayVector = normalize(lightDirection + input.viewDir);float3 binormalDirection = cross(input.normalDir, input.tangentDir);float dotLN = dot(lightDirection, input.normalDir); // compute this dot product only oncefloat3 ambientLighting = UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;float3 diffuseReflection = attenuation * _LightColor0.rgb * _Color.rgb * max(0.0, dotLN);float3 specularReflection;if (dotLN < 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{float dotHN = dot(halfwayVector, input.normalDir);float dotVN = dot(input.viewDir, input.normalDir);float dotHTAlphaX = dot(halfwayVector, input.tangentDir) / _AlphaX;float dotHBAlphaY = dot(halfwayVector, binormalDirection) / _AlphaY;specularReflection = attenuation * _LightColor0.rgb * _SpecColor.rgb * sqrt(max(0.0, dotLN / dotVN)) * exp(-2.0 * (dotHTAlphaX * dotHTAlphaX + dotHBAlphaY * dotHBAlphaY) / (1.0 + dotHN));}return float4(ambientLighting + diffuseReflection + specularReflection, 1.0);}ENDCG}Pass {    Tags { "LightMode" = "ForwardAdd" } // pass for additional light sourcesBlend One One // additive blending CGPROGRAM#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 _AlphaX;uniform float _AlphaY;struct vertexInput {float4 vertex : POSITION;float3 normal : NORMAL;float4 tangent : TANGENT;};struct vertexOutput {float4 pos : SV_POSITION;float4 posWorld : TEXCOORD0;// position of the vertex (and fragment) in world space float3 viewDir : TEXCOORD1;// view direction in world spacefloat3 normalDir : TEXCOORD2;// surface normal vector in world spacefloat3 tangentDir : TEXCOORD3;// brush direction in world space};vertexOutput vert(vertexInput input) {vertexOutput output;float4x4 modelMatrix = _Object2World;float4x4 modelMatrixInverse = _World2Object;output.posWorld = mul(modelMatrix, input.vertex);output.viewDir = normalize(_WorldSpaceCameraPos - output.posWorld.xyz);output.normalDir = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);output.tangentDir = normalize(mul(modelMatrix, float4(input.tangent.xyz, 0.0)).xyz);output.pos = mul(UNITY_MATRIX_MVP, input.vertex);return output;}float4 frag(vertexOutput input) : COLOR{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 halfwayVector = normalize(lightDirection + input.viewDir);float3 binormalDirection = cross(input.normalDir, input.tangentDir);float dotLN = dot(lightDirection, input.normalDir); // compute this dot product only oncefloat3 diffuseReflection = attenuation * _LightColor0.rgb * _Color.rgb * max(0.0, dotLN);float3 specularReflection;if (dotLN < 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{float dotHN = dot(halfwayVector, input.normalDir);float dotVN = dot(input.viewDir, input.normalDir);float dotHTAlphaX = dot(halfwayVector, input.tangentDir) / _AlphaX;float dotHBAlphaY = dot(halfwayVector, binormalDirection) / _AlphaY;specularReflection = attenuation * _LightColor0.rgb * _SpecColor.rgb * sqrt(max(0.0, dotLN / dotVN)) * exp(-2.0 * (dotHTAlphaX * dotHTAlphaX + dotHBAlphaY * dotHBAlphaY) / (1.0 + dotHN));}return float4(diffuseReflection + specularReflection, 1.0);}ENDCG}}Fallback "Specular"
}

总结

恭喜,你完成了一个相当高级的教程!我们看到了:

  • 什么是BRDF(双向反射分布函数)。
  • 什么是各向异性反射的Ward BRDF模型。
  • 如何实现Ward的BRDF模型。

这篇关于Cg Programming/Unity/Brushed Metal拉丝金属的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

【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

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

unity开发 --------- NGUI (UIViewPort、UIDraggableCamera)

unity开发 --------- NGUI 前面提到一种实现ScrollView的方法:unity开发 --------- NGUI (UIDragPanelContents、UIDraggablePanel、UICenterOnChild、UIScollBar、SpringPanel) 但上面那种发放有一个缺陷!它要用到shader。也就是说,对于低端设备,就不能以上面那种方式实现拖