Cg Programming In Unity Diffuse Reflection(Wiki翻译自用)

2023-10-10 00:30

本文主要是介绍Cg Programming In Unity Diffuse Reflection(Wiki翻译自用),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Contents

  • 漫反射
  • 单个定向光源(平行光)的shader代码
  • Fallback Shaders
  • 多个平行(像素)光的shader代码
  • 点光源变化
  • 聚光灯变化
  • 总结

本教程介绍了每个顶点的漫反射。
这是有关Unity中基本照明的一系列教程中的第一篇。 在本教程中,我们从单个方向性光源的漫反射开始,然后包括点光源和多个光源(使用多次通过)。 进一步的教程将对此进行扩展,特别是镜面反射,每个像素光照和双面光照。

漫反射

在这里插入图片描述月球几乎只表现出漫反射(也被称为兰伯特反射,),即光被反射到所有方向而没有镜面反射高光(理想的漫反射)。此类材质的其他示例是粉笔和磨砂纸。实际上,任何看起来暗淡无光的表面都是漫反射。在完全漫反射的情况下,观察到的反射光的强度取决于表面法线矢量和入射光夹角的余弦。如左图所示,通常考虑从计算照明的曲面上一点开始的归一化矢量:归一化表面法线向量N垂直于表面,归一化光照方向L指向光源。
对于观察到的漫反射光 I d i f f u s e I_{diffuse} Idiffuse 我们需要归一化表面法线向量N与光源L归一化方向之间的夹角余弦,即点积N·L,因为 两个向量a和b的点积a·b为: a ⋅ b = ∣ a ∣ ∣ b ∣ cos ⁡ ∡ ( a , b ) a\cdot b = |a||b|\cos \measuredangle(a,b) ab=abcos(a,b)
对于归一化向量,长度 ∣ a ∣ |a| a ∣ b ∣ |b| b都是1。
如果点积 N ⋅ L N·L NL为负,那么光源在表面”错误“的一侧,我们应该将反射设置为0。可以通过 m a x ( 0 , N ⋅ L ) max(0,N·L) max(0,NL)保证点积的结果不为负,当点积结果为负时固定为0。此外,反射光取决于入射光强度 I i n c o m i n g I_{incoming} Iincoming和材质反射常量 k d i f f u s e k_{diffuse} kdiffuse:对于黑色表面, k d i f f u s e = 0 k_{diffuse}=0 kdiffuse=0,对于白色表面, k d i f f u s e = 1 k_{diffuse}=1 kdiffuse=1
漫反射公式为:
*

I d i f f u s e = I i n c o m i n g k d i f f u s e m a x ( 0 , N ⋅ L ) I_{diffuse}=I_{incoming}k_{diffuse}max(0,N·L) Idiffuse=Iincomingkdiffusemax(0,NL)

对于彩色光,该方程适应于每种颜色的分量(例如,红,绿,蓝)。因此,如果 I d i f f u s e I_{diffuse} Idiffuse, I i n c o m i n g I_{incoming} Iincoming k d i f f u s e k_{diffuse} kdiffuse表示颜色矢量,并且乘法是逐分量执行的(它们用于Cg中的矢量),此等式也适用于彩色光。 这就是我们在着色器代码中实际使用的东西。

单个定向光源(平行光)的shader代码

如果我们只有一个平行光,则用于为 I d i f f u s e I_{diffuse} Idiffuse实现方程式的shader代码相对较小。为了实现方程式,我们遵循有关实现方程式的问题:

  • 应该在顶点着色器还是片元着色器中实现方程?我们在这里尝试在顶点着色器。 在“平滑镜面高光”部分,我们将介绍片段着色器中的实现。
  • 应该在哪个坐标系中实现方程式? 我们默认在Unity中使用世界空间。(事实证明,这是一个不错的选择,因为Unity在世界空间中提供了光的方向)。
  • 我们从哪里获得参数?这个答案有点长:
    我们使用一个shader属性使用户指定漫反射材质颜色 k d i f f u s e k_{diffuse} kdiffuse。我们可以通过Unity内置的参数_WorldSpaceLightPos0获得指向世界空间中光源的方向,通过内置参数_LightColor0获得光照颜色 I i n c o m i n g I_{incoming} Iincoming。如”Shading in World Space“所描述的,我们必须使用标签
    Tags {"LightMode" = "ForwardBase"}标记着色器通道,以使得这些内置的uniform参数具有正确的值。(之后我们将讨论此标签的实际含义)。我们从具有Normal语义的顶点输入参数获得在对象空间中的表面法线向量。由于我们在世界空间中实现此方程,因此必须按照”Silhouette Enhancement“一节中所讨论的一样,将法线向量变换到世界空间。
    shader代码如下:
Shader "Cg per-vertex diffuse lighting" {Properties {_Color ("Diffuse Material Color", Color) = (1,1,1,1) }SubShader {Pass {	Tags { "LightMode" = "ForwardBase" } // make sure that all uniforms are correctly setCGPROGRAM#pragma vertex vert  #pragma fragment frag #include "UnityCG.cginc"uniform float4 _LightColor0; // color of light source (from "UnityLightingCommon.cginc")uniform float4 _Color; // define shader property for shadersstruct vertexInput {float4 vertex : POSITION;float3 normal : NORMAL;};struct vertexOutput {float4 pos : SV_POSITION;float4 col : COLOR;};vertexOutput vert(vertexInput input) {vertexOutput output;float4x4 modelMatrix = unity_ObjectToWorld;float4x4 modelMatrixInverse = unity_WorldToObject;float3 normalDirection = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);// alternative: // float3 normalDirection = UnityObjectToWorldNormal(input.normal);float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);float3 diffuseReflection = _LightColor0.rgb * _Color.rgb* max(0.0, dot(normalDirection, lightDirection));output.col = float4(diffuseReflection, 1.0);output.pos = UnityObjectToClipPos(input.vertex);return output;}float4 frag(vertexOutput input) : COLOR{return input.col;}ENDCG}}Fallback "Diffuse"
}

使用此着色器时,请确保场景中只有一个光源,该光源必须是平行光。

Fallback Shaders

shader代码中的Fallback“ Diffuse”行定义了一个内置的Fallback着色器,以防Unity找不到合适的子着色器。 对于我们的示例,如果Unity不使用前向渲染路径(请参见下文),则它将使用Fallback shader。通过为我们的着色器属性选择特定名称“ _Color”,确保此内置Fallback Shader也可以访问它。内置着色器的源代码可在Unity网站上获得。检查此源代码似乎是确定合适的Fallback Shader及其使用的属性名称的唯一方法。

多个平行(像素)光的shader代码

到目前为止,我们仅考虑了单个光源。 为了处理多个光源,Unity根据渲染和质量设置选择各种技巧。在此处的教程中,我们将仅介绍“前向渲染路径”。(此外,应将所有摄像机配置为使用播放器设置,这是默认设置。)
在本教程中,我们仅考虑Unity的所谓像素灯。 对于第一个像素光(也就是平行光),Unity调用shader Pass的标记Tags { "LightMode" = "ForwardBase" }(如上面代码所示)。对于每增加一个平行光,Unity调用shader Pass的标记Tags { "LightMode" = "ForwardAdd" },为了确保所有光源都渲染为像素光源,必须确保质量设置允许足够的像素光源:选择Edit > Project Settings > Quality,然后在您使用的任何质量设置中增加标记为Pixel Light Count的数字。如果场景中的光源多于像素光数量允许的范围,那么Untiy仅将最重要的光作为像素光进行渲染。或者,您可以将Render Mode 设置为Important,以将其渲染为像素光源。
到目前为止,对于ForwardBasePass,我们的着色器代码还可以。对于ForwardAddPass,我们需要将反射光添加到已经存储在帧缓冲区的光中。为此,我们只需要配置 blend 即可将新的片元输出颜色添加到帧缓冲区中的颜色。如“Transparency”部分所述,这是通过以下行指定的加法混合方程实现的:

Blend One One

Blend自动将所有结果固定在0到1之间,因此我们不需要担心颜色或者alpha值大于1。
总而言之,我们用于多个定向光源的新着色器为:

Shader "Cg per-vertex diffuse lighting" {Properties {_Color ("Diffuse Material Color", Color) = (1,1,1,1) }SubShader {Pass {	Tags { "LightMode" = "ForwardBase" } // pass for first light sourceCGPROGRAM#pragma vertex vert  #pragma fragment frag #include "UnityCG.cginc"uniform float4 _LightColor0; // color of light source (from "UnityLightingCommon.cginc")uniform float4 _Color; // define shader property for shadersstruct vertexInput {float4 vertex : POSITION;float3 normal : NORMAL;};struct vertexOutput {float4 pos : SV_POSITION;float4 col : COLOR;};vertexOutput vert(vertexInput input) {vertexOutput output;float4x4 modelMatrix = unity_ObjectToWorld;float4x4 modelMatrixInverse = unity_WorldToObject; float3 normalDirection = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);float3 diffuseReflection = _LightColor0.rgb * _Color.rgb* max(0.0, dot(normalDirection, lightDirection));output.col = float4(diffuseReflection, 1.0);output.pos = UnityObjectToClipPos(input.vertex);return output;}float4 frag(vertexOutput input) : COLOR{return input.col;}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 "UnityLightingCommon.cginc")uniform float4 _Color; // define shader property for shadersstruct vertexInput {float4 vertex : POSITION;float3 normal : NORMAL;};struct vertexOutput {float4 pos : SV_POSITION;float4 col : COLOR;};vertexOutput vert(vertexInput input) {vertexOutput output;float4x4 modelMatrix = unity_ObjectToWorld;float4x4 modelMatrixInverse = unity_WorldToObject; float3 normalDirection = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);float3 diffuseReflection = _LightColor0.rgb * _Color.rgb* max(0.0, dot(normalDirection, lightDirection));output.col = float4(diffuseReflection, 1.0);output.pos = UnityObjectToClipPos(input.vertex);return output;}float4 frag(vertexOutput input) : COLOR{return input.col;}ENDCG}}Fallback "Diffuse"
}

这似乎是一个相当长的着色器。但是,除了Tags和ForwardAdd Pass中的Blend设置之外,两个Pass都是相同的。

点光源变化

对于平行光源,_WorldSpaceLightPos0指定了光源入射方向。但是,对于点光源,_WorldSpaceLightPos0指定了光源在世界空间中的位置。我们必须计算光源方向为世界空间中,顶点坐标到光源位置的差向量。因为点的第四坐标为1(点光源),方向的第四坐标为0(平行光),我们可以轻松区分这两种情况:

			float3 lightDirection;if (0.0 == _WorldSpaceLightPos0.w) // directional light?{lightDirection = normalize(_WorldSpaceLightPos0.xyz);} else // point or spot light{lightDirection = normalize(_WorldSpaceLightPos0.xyz - mul(modelMatrix, input.vertex).xyz);}

虽然平行光没有光的衰减,但我们应该增加点光源随距离的衰减。当光从一个点在三个维度上扩散时,它以更大的距离覆盖了更大的虚拟球体。由于这些球体的表面随半径的增加而呈平方增加,因此,每个区域的光量随与点光源的距离增加而呈二次方减少。因此,我们应将光源的强度除以到顶点的距离的平方。
由于二次衰减相当快,因此我们使用随距离变化的线性衰减,即将强度除以距离而不是平方距离。 代码可以是:

			float3 lightDirection;float attenuation;if(0.0 == _WorldSpaceLightPos0.w){attenuation = 1.0;lightDirection = normalize(_WorldSpaceLightPos0.xyz);}else{float3 vertexToLightSource = _WorldSpaceLightPos0.xyz- mul(unity_ObjectToWorld,input.vertex).xyz;float distance = length(vertexToLightSource );attenuation = 1.0/distance;lightDirection = normalize(vertexToLightSource);}

然后将衰减因子attenuation 乘以_LightColor0来计算入射光。请参见下面的着色器代码。请注意,点光源具有其他功能,这些功能超出了本教程的范围。
另外请注意,此代码不太可能为您提供最佳性能,因为任何if通常都非常昂贵。由于_WorldSpaceLightPos0.w为0或1,因此实际上不必重写代码即可避免使用if并进一步优化代码:

			float3 vertexToLightSource = _WorldSpaceLightPos0.xyz - mul(unity_ObjectToWorld, input.vertex * _WorldSpaceLightPos0.w).xyz ;float one_over_distance = 1.0 / length (vertexToLightSource);float attenuation = lerp(1.0, one_over_distance, _WorldSpaceLightPos0.w); float3 lightDirection =  vertexToLightSource * one_over_distance;

但是,为清楚起见,我们将使用带有if的版本。
多个定向和点光源的完整着色器代码为:

Shader "Cg per-vertex diffuse lighting" {Properties {_Color ("Diffuse Material Color", Color) = (1,1,1,1) }SubShader {Pass {	Tags { "LightMode" = "ForwardBase" } // pass for first light sourceCGPROGRAM#pragma vertex vert  #pragma fragment frag #include "UnityCG.cginc"uniform float4 _LightColor0; // color of light source (from "UnityLightingCommon.cginc")uniform float4 _Color; // define shader property for shadersstruct vertexInput {float4 vertex : POSITION;float3 normal : NORMAL;};struct vertexOutput {float4 pos : SV_POSITION;float4 col : COLOR;};vertexOutput vert(vertexInput input) {vertexOutput output;float4x4 modelMatrix = unity_ObjectToWorld;float4x4 modelMatrixInverse = unity_WorldToObject; float3 normalDirection = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).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 - mul(modelMatrix, input.vertex).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));output.col = float4(diffuseReflection, 1.0);output.pos = UnityObjectToClipPos(input.vertex);return output;}float4 frag(vertexOutput input) : COLOR{return input.col;}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 "UnityLightingCommon.cginc")uniform float4 _Color; // define shader property for shadersstruct vertexInput {float4 vertex : POSITION;float3 normal : NORMAL;};struct vertexOutput {float4 pos : SV_POSITION;float4 col : COLOR;};vertexOutput vert(vertexInput input) {vertexOutput output;float4x4 modelMatrix = unity_ObjectToWorld;float4x4 modelMatrixInverse = unity_WorldToObject;float3 normalDirection = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).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 - mul(modelMatrix, input.vertex).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));output.col = float4(diffuseReflection, 1.0);output.pos = UnityObjectToClipPos(input.vertex);return output;}float4 frag(vertexOutput input) : COLOR{return input.col;}ENDCG}}Fallback "Diffuse"
}

请注意,ForwardBasePass中的光源始终是定向光。 因此,实际上可以简化第一遍的代码。 另一方面,在两次Pass中使用相同的Cg代码,可以更轻松地将代码从一个Pass复制并粘贴到另一个Pass,以防万一我们必须编辑着色器代码。

聚光灯变化

Unity借助“ Cookies”部分中所述的cookie纹理实现聚光灯; 但是,这有些超前。 在这里,我们将聚光灯视为点光源。

总结

您刚刚了解了Unity的每个像素灯光如何工作。 对于以后有关更高级照明的教程,这是必不可少的。 我们还看到了:

  • 什么是漫反射以及如何进行数学描述( I d i f f u s e = I i n c o m i n g k d i f f u s e m a x ( 0 , N ⋅ L ) I_{diffuse}=I_{incoming}k_{diffuse}max(0,N·L) Idiffuse=Iincomingkdiffusemax(0,NL))。
  • 如何在着色器中为单个平行光实现漫反射。
  • 如何扩展具有线性衰减的点光源的着色器。
  • 如何进一步扩展着色器以处理多个逐像素的光照。

这篇关于Cg Programming In Unity Diffuse Reflection(Wiki翻译自用)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

论文翻译:arxiv-2024 Benchmark Data Contamination of Large Language Models: A Survey

Benchmark Data Contamination of Large Language Models: A Survey https://arxiv.org/abs/2406.04244 大规模语言模型的基准数据污染:一项综述 文章目录 大规模语言模型的基准数据污染:一项综述摘要1 引言 摘要 大规模语言模型(LLMs),如GPT-4、Claude-3和Gemini的快

论文翻译:ICLR-2024 PROVING TEST SET CONTAMINATION IN BLACK BOX LANGUAGE MODELS

PROVING TEST SET CONTAMINATION IN BLACK BOX LANGUAGE MODELS https://openreview.net/forum?id=KS8mIvetg2 验证测试集污染在黑盒语言模型中 文章目录 验证测试集污染在黑盒语言模型中摘要1 引言 摘要 大型语言模型是在大量互联网数据上训练的,这引发了人们的担忧和猜测,即它们可能已

excel翻译软件有哪些?如何高效提翻译?

你是否曾在面对满屏的英文Excel表格时感到头疼?项目报告、数据分析、财务报表... 当这些重要的信息被语言壁垒阻挡时,效率和理解度都会大打折扣。别担心,只需3分钟,我将带你轻松解锁excel翻译成中文的秘籍。 无论是职场新人还是老手,这一技巧都将是你的得力助手,让你在信息的海洋中畅游无阻。 方法一:使用同声传译王软件 同声传译王是一款专业的翻译软件,它支持多种语言翻译,可以excel

MonoHuman: Animatable Human Neural Field from Monocular Video 翻译

MonoHuman:来自单目视频的可动画人类神经场 摘要。利用自由视图控制来动画化虚拟化身对于诸如虚拟现实和数字娱乐之类的各种应用来说是至关重要的。已有的研究试图利用神经辐射场(NeRF)的表征能力从单目视频中重建人体。最近的工作提出将变形网络移植到NeRF中,以进一步模拟人类神经场的动力学,从而动画化逼真的人类运动。然而,这种流水线要么依赖于姿态相关的表示,要么由于帧无关的优化而缺乏运动一致性

Unity Post Process Unity后处理学习日志

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

linux dlopen手册翻译

名称 dlclose, dlopen, dlmopen 打开和关闭一个共享对象 简介 #include <dlfcn.h>void *dlopen(const char*filename, int flags);int dlclose(void *handle);#define _GNU_SOURCE#include <dlfcn.h>void *dlmoopen(Lmid_t lm

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

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

从计组中从重温C中浮点数表示及C程序翻译过程

目录 移码​编辑  传统浮点表示格式 浮点数的存储(ieee 754)->修炼内功 例子:   ​编辑 浮点数取的过程   C程序翻译过程 移码  传统浮点表示格式 浮点数的存储(ieee 754)->修炼内功 根据国际标准IEEE(电⽓和电⼦⼯程协会)  32位 例子:    64位    IEEE754对有效数字M和

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