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 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中的编码格式,下面我介绍一种简单快

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

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(2) (*****生成数据结构类的方式特别有趣****)-CSDN博客 做完了数据结构类,该做一个存储类了,也就是生成一个字典类(只是声明)  实现和上一节的数据结构类的方式大同小异,所

【Unity小技巧】URP管线遮挡高亮效果

前言 在URP渲染管线环境下实现物体遮挡高亮显示效果,效果如下: Unity URP遮挡高亮 实现步骤 创建层级,为需要显示高亮效果的物体添加层级,比如Player 创建一个材质球,也就是高亮效果显示的材质球找到Universal Renderer Data Assets 4.在Assets上添加两个Render Objects组件 第一个做如下三处设置 指定遮挡层级指

【Unity面经】实习篇:面试官常问的一百个面试题

👨‍💻个人主页:@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 专栏交流🧧🟥Unity100个实战基础✨🎁🟦 Unity100个精华一记✨🎁🟩 Unity50个demo案例教程✨🎁🟨 Unity100个精华细节BUG✨🎁🟨 Unity100个面试题✨🎁 文章