Unity中Shader不同灯光类型的支持与区分

news2024/11/14 4:18:49

文章目录

  • 前言
  • 一、在开始之前做一些准备
    • 1、在上一篇文章的场景基础上,增加一个Unity默认的球体作为对照组
    • 2、创建一个点光源,用来看点光源的影响 对 Unity默认的Shader效果 和 我们实现的Shader效果 之间的不同
  • 二、点光源的适配
    • 把上一篇文章中 ForwardBase 的 Pass 复制粘贴 到 与 该Pass平行的程序块,然后再对其做之后点光源的灯光适配(因为点光源 和 聚光灯效果,是在ForwordAdd中实现的)
    • 按上面步骤修改后,小球变的 受点光源的影响 又受 主平行光的影响 ![请添加图片描述](https://img-blog.csdnimg.cn/ffaccd6cfcea4830b694372b23d36a5a.gif)
  • 三、不同灯光类型的支持与区分
    • 1、我们加入一个聚光灯
    • 2、使用内置的宏定义生成Shader变体来区分是什么类型的光照
    • 3、剔除无用的变体,节省性能
  • 最终测试代码:


前言

Unity中Shader不同灯光类型的支持与区分


一、在开始之前做一些准备

1、在上一篇文章的场景基础上,增加一个Unity默认的球体作为对照组

创建前:
在这里插入图片描述
创建后:
在这里插入图片描述

2、创建一个点光源,用来看点光源的影响 对 Unity默认的Shader效果 和 我们实现的Shader效果 之间的不同

二、点光源的适配

把上一篇文章中 ForwardBase 的 Pass 复制粘贴 到 与 该Pass平行的程序块,然后再对其做之后点光源的灯光适配(因为点光源 和 聚光灯效果,是在ForwordAdd中实现的)

Shader "MyShader/P1_5_4"
{
    Properties
    {
        //光照系数
        _DiffuseIntensity("Diffuse Intensity",float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        
        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                //在应用程序阶段传入到顶点着色器中,时加入顶点法向量信息
                half3 normal:NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                //定义一个3维向量,用于接受世界坐标顶点法向量信息
                half3 worldNormal:TEXCOORD1;
                
            };

            half _DiffuseIntensity;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                //把顶点法线本地坐标转化为世界坐标
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //Lambert光照模型的结果
                //Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L))
                //使用 Unity 封装的参数 获取环境光色
                float Ambient = unity_AmbientSky;

                //在属性面板定义一个 可调节的参数 用来作为光照系数,调节效果的强弱
                half Kd = _DiffuseIntensity;

                //获取主平行光的颜色
                fixed4 LightColor = _LightColor0;

                //获取顶点法线坐标(让其归一化)
                fixed3 N = normalize(i.worldNormal);

                //获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)
                fixed3 L = _WorldSpaceLightPos0;

                //使用Lambert公式计算出光照
                //fixed4 Diffuse = Ambient + (Kd * LightColor * dot(N,L));
                //因为 当 顶点法线 与 反射点指向光源的向量 垂直 或成钝角时,光照效果就该忽略不计
                //所以,这里使用 max(a,b)函数来限制 点积的结果范围
                fixed4 Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L));
                
                return Diffuse;
            }
            ENDCG
        }
        Pass
        {
            Tags{"LightMode"="ForwardAdd"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                //在应用程序阶段传入到顶点着色器中,时加入顶点法向量信息
                half3 normal:NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                //定义一个3维向量,用于接受世界坐标顶点法向量信息
                half3 worldNormal:TEXCOORD1;
                
            };

            half _DiffuseIntensity;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                //把顶点法线本地坐标转化为世界坐标
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //Lambert光照模型的结果
                //Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L))
                //使用 Unity 封装的参数 获取环境光色
                float Ambient = unity_AmbientSky;

                //在属性面板定义一个 可调节的参数 用来作为光照系数,调节效果的强弱
                half Kd = _DiffuseIntensity;

                //获取主平行光的颜色
                fixed4 LightColor = _LightColor0;

                //获取顶点法线坐标(让其归一化)
                fixed3 N = normalize(i.worldNormal);

                //获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)
                fixed3 L = _WorldSpaceLightPos0;

                //使用Lambert公式计算出光照
                //fixed4 Diffuse = Ambient + (Kd * LightColor * dot(N,L));
                //因为 当 顶点法线 与 反射点指向光源的向量 垂直 或成钝角时,光照效果就该忽略不计
                //所以,这里使用 max(a,b)函数来限制 点积的结果范围
                fixed4 Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L));
                
                return Diffuse;
            }
            ENDCG
        }
    }
}

把复制后的光照模式改为ForwardAdd

Tags{“LightMode”=“ForwardAdd”}

修改后,点光源对我们的小球已经有了初步的影响
请添加图片描述
但是,会发现不受主平行光的影响了,所以需要进行修改

并且,由于默认的混合模式为 Blend One Zero。
渲染时,由于主平行光先渲染,点光源后渲染,所以颜色缓冲区会被后渲染的点光源覆盖。
所以修改ForwordAdd 的 Pass 中 混合模式为 Blend One One

Blend One One

因为计算点光源时不需要考虑环境光,所以在Lambert光照模型中删除环境光的影响

在ForwardAdd的Pass中的片元着色器中,把最后的输出结果修改为如下:

fixed4 Diffuse = LightColor * max(0,dot(N,L));

把片元着色器简化为:

fixed4 frag (v2f i) : SV_Target
            {
                //获取主平行光的颜色
                fixed4 LightColor = _LightColor0;
                //获取顶点法线坐标(让其归一化)
                fixed3 N = normalize(i.worldNormal);
                //获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)
                fixed3 L = _WorldSpaceLightPos0;
                //因为计算点光源时不需要考虑环境光,所以在Lambert光照模型中删除环境光的影响
                fixed4 Diffuse = LightColor * max(0,dot(N,L));
                
                return Diffuse;
            }

按上面步骤修改后,小球变的 受点光源的影响 又受 主平行光的影响
请添加图片描述

三、不同灯光类型的支持与区分

1、我们加入一个聚光灯

在这里插入图片描述
加入聚光灯后,会发现小球只渲染了聚光灯的效果
原因是:
目前场景只支持一盏逐像素灯,在 聚光灯 和 点光源 之间,谁的强度大,谁就变成逐像素灯

请添加图片描述

2、使用内置的宏定义生成Shader变体来区分是什么类型的光照

#pragma multi_compile_fwdadd
定义在LightMode=ForwardAdd的Pass中,在此Pass中用来计算其它的逐像素光照.而此指令的作用是一次性生成Unity在ForwardAdd中需要的各种内置宏.
DIRECTIONAL DIRECTIONAL_COOKIE POINT POINT_COOKIE SPOT

  1. DIRECTIONAL :判断当前灯是否为平行灯.
  2. DIRECTIONAL_COOKIE :判断当前灯是否为Cookie平行灯.
  3. POINT :判断当前灯是否为点灯.
  4. POINT_COOKIE :判断当前灯是否为Cookie点灯.
  5. SPOT :判断当前灯是否为聚光灯.

在 ForwardAdd 的 Pass 中加入这条宏

#pragma multi_compile_fwdadd

然后,我看可以看见该Shader生成了6个变体
在这里插入图片描述

在这里插入图片描述

我们在片元着色器中,测试使用一下这些变体
1、当为点光源时,返回绿色

fixed4 frag (v2f i) : SV_Target
            {
                #if POINT
                return fixed4(0,1,0,1);
                #endif

                
                //获取主平行光的颜色
                fixed4 LightColor = _LightColor0;
                //获取顶点法线坐标(让其归一化)
                fixed3 N = normalize(i.worldNormal);
                //获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)
                fixed3 L = _WorldSpaceLightPos0;
                //因为计算点光源时不需要考虑环境光,所以在Lambert光照模型中删除环境光的影响
                fixed4 Diffuse = LightColor * max(0,dot(N,L));
                
                return Diffuse;
                
            }

请添加图片描述

2、当为点光源时,返回黑色

fixed4 frag (v2f i) : SV_Target
            {
                #if POINT
                return fixed4(0,1,0,1);
                #elif SPOT
                return 0;
                #endif

                
                //获取主平行光的颜色
                fixed4 LightColor = _LightColor0;
                //获取顶点法线坐标(让其归一化)
                fixed3 N = normalize(i.worldNormal);
                //获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)
                fixed3 L = _WorldSpaceLightPos0;
                //因为计算点光源时不需要考虑环境光,所以在Lambert光照模型中删除环境光的影响
                fixed4 Diffuse = LightColor * max(0,dot(N,L));
                
                return Diffuse;
                
            }

请添加图片描述

3、剔除无用的变体,节省性能

因为Shader变体的数量是一般是倍数增加,所以在设计时,就要尽量减少Shader的变体数量
Shader变体的数量,会直接影响 ShaderLab 的内存,打包到手机会影响到 Native 内存

法一:手动声明我们需要的变体
#pragma multi_compile POINT SPOT	

在这里插入图片描述
在这里插入图片描述

法二:剔除不需要的变体
#pragma skip_variants XXX01 XXX02...
剔除指定的变体,可同时剔除多个

#pragma skip_variants DIRECTIONAL POINT_COOKIE DIRECTIONAL_COOKIE

效果是一样的:
在这里插入图片描述

最终测试代码:

Shader "MyShader/P1_5_4"
{
    Properties
    {
        //光照系数
        _DiffuseIntensity("Diffuse Intensity",float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        
        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                //在应用程序阶段传入到顶点着色器中,时加入顶点法向量信息
                half3 normal:NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                //定义一个3维向量,用于接受世界坐标顶点法向量信息
                half3 worldNormal:TEXCOORD1;
                
            };

            half _DiffuseIntensity;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                //把顶点法线本地坐标转化为世界坐标
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //Lambert光照模型的结果
                //Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L))
                //使用 Unity 封装的参数 获取环境光色
                float Ambient = unity_AmbientSky;

                //在属性面板定义一个 可调节的参数 用来作为光照系数,调节效果的强弱
                half Kd = _DiffuseIntensity;

                //获取主平行光的颜色
                fixed4 LightColor = _LightColor0;

                //获取顶点法线坐标(让其归一化)
                fixed3 N = normalize(i.worldNormal);

                //获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)
                fixed3 L = _WorldSpaceLightPos0;

                //使用Lambert公式计算出光照
                //fixed4 Diffuse = Ambient + (Kd * LightColor * dot(N,L));
                //因为 当 顶点法线 与 反射点指向光源的向量 垂直 或成钝角时,光照效果就该忽略不计
                //所以,这里使用 max(a,b)函数来限制 点积的结果范围
                fixed4 Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L));
                
                return Diffuse;
            }
            ENDCG
        }
        Pass
        {
            Tags{"LightMode"="ForwardAdd"}
            Blend One One
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //加入Unity自带的宏,用于区分不同的光照
            //只声明我们需要的变体
            //#pragma multi_compile POINT SPOT
            
            #pragma multi_compile_fwdadd
            //剔除我们不需要的变体
            #pragma skip_variants DIRECTIONAL POINT_COOKIE DIRECTIONAL_COOKIE
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                //在应用程序阶段传入到顶点着色器中,时加入顶点法向量信息
                half3 normal:NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                //定义一个3维向量,用于接受世界坐标顶点法向量信息
                half3 worldNormal:TEXCOORD1;
                
            };

            half _DiffuseIntensity;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                //把顶点法线本地坐标转化为世界坐标
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                #if POINT
                return fixed4(0,1,0,1);
                #elif SPOT
                return 0;
                #endif

                
                //获取主平行光的颜色
                fixed4 LightColor = _LightColor0;
                //获取顶点法线坐标(让其归一化)
                fixed3 N = normalize(i.worldNormal);
                //获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)
                fixed3 L = _WorldSpaceLightPos0;
                //因为计算点光源时不需要考虑环境光,所以在Lambert光照模型中删除环境光的影响
                fixed4 Diffuse = LightColor * max(0,dot(N,L));
                
                return Diffuse;
                
            }
            ENDCG
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1079658.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

16基于otsuf方法的图像分割,程序已调通,可更换自己的图片进行分割,程序具有详细的代码注释,可轻松掌握。基于MATLAB平台,需要直接拍下。

基于otsuf方法的图像分割,程序已调通,可更换自己的图片进行分割,程序具有详细的代码注释,可轻松掌握。基于MATLAB平台,需要直接拍下。 16matlab图像处理图像分割 (xiaohongshu.com)

微宏科技基于 KubeSphere 的微服务架构实践

作者:尹珉,KubeSphere Ambassador、contributor,KubeSphere 社区用户委员会杭州站站长。 公司简介 杭州微宏科技有限公司于 2012 年成立,专注于业务流程管理和自动化(BPM&BPA)软件研发和解决方案供应商。创始团队毕业于浙江大…

three.js学习之vR展厅

目标 1、需要会的知识点three.js的场景,摄像机,渲染器,轨道控制器,坐标轴,场景适配,渲染循环创建立方缓冲几何体、纹理、3d物体 实现:创建立方几何体,纹理贴图镜面反向渲染&#xf…

空转旋转 seurat spatial rotate 图片 翻转 数据结构 对象 seurat的空转数据存储

1seurat 取子集操作 3. 对象操作 ① 通过结构图上的,$符号依次取 ② 两个中括号操作,pbmc[[ ]]。 教程中,pbmc[[percent.MT]]向meta.data添加 percent.MT 这一列。 pbmc[[]],中括号取的是上面结构图中的二级数据名称以上两种方法的区别是&am…

《Unity Shader入门精要》笔记06

基础纹理 单张纹理纹理的属性Alpha SourceWrap ModeFilter Mode 凹凸映射高度纹理法线纹理实践在切线空间下计算在世界空间下计算 Unity中的法线纹理类型Create from Grayscale 渐变纹理遮罩纹理其他遮罩处理 单张纹理 我们通常会使用一张纹理来代替物体的漫反射颜色 Shader …

[SRT]1.协议简介

1.简介 ​ 安全可靠传输协议(Secure Reliable Transport)简称SRT,是一种基于UDT协议的开源互联网传输协议,Haivision和Wowza合作成立SRT联盟,管理和支持SRT协议开源应用的组织,这个组织致力于促进视频流解决方案的互通性&a…

041:mapboxGL移动到到某Layer上,更换鼠标形状

第041个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中通过鼠标hover的方式来更换鼠标形状。 通过mouseenter和mouseleave的方法,经过某个图层上的时候,更换鼠标的形状,从default到pointer。 离开后从pointer到default。 直接复制下面的 vue+mapbox源代码,操…

安信证券携手共议量化行业的赋能发展

9月22日,安信证券上海浦西分公司携手非凸科技、通联数据在上海共同开展“量化私募闭门交流会”,与资方管理人就如何赋能量化私募可持续发展,给出了精彩纷呈的见解与讨论。 安信证券以“打造数字化券商”为目标,加强科技创新与业务…

数据库常见面试题--MySQL

梳理面试过程中数据库相关的常见问题,需要说明的是,这篇文章主要是基于MySQL数据库,其他类型的数据库还请自行参考使用。 数据库概述 为什么使用数据库 1、数据库增删改查更方便 2、提供了事务的能力 本质是更好的管理数据。 数据库体系结…

Android rtmp 低延迟直播方案:简介

Android rtmp 低延迟直播方案:简介 Android RTMP 低延迟直播方案:使用 RTMP 推送至 ZLMediaKit,通过 WebRTC 进行拉流。

会展购票系统有哪些特点?如何选择好的会展购票系统开发公司

会展购票系统是会展行业的重要组成部分,它具有提高会展业务流程运转效率、业务操作方便快捷、降低人工成本等优势。如开利网络自主研发的会展购票系统就有以下几个特点: 1. 拥有多端开发能力。除了电脑端外,还可以开发微信小程序端、公众号端…

不说废话,推荐一款超实用免费配音软件~

短视频发展如火如荼,在制作编辑视频的同时,文字转语音的需求也越来越大,例如解说配音、旁白配音、vlog配音等,今天就给大家推荐一款超实用的免费配音软件,感兴趣的小伙伴请接着往下看! 一、悦音AI配音&…

语音芯片的“等级”之分

语音芯片,你或许不晓得这个“芯”也是有高低之分,你可能听说过手机“发烧级”高性能芯片,同样在语音芯片中存在着性能不同等级的语音芯片。一般我们将普通芯片主要分为3个等级:商业级(又称民用级)、工业级和…

在Scrum敏捷开发中,开发人员(Developers)的职责

在Scrum敏捷开发中,开发人员(Developers)是Scrum团队中最重要的角色之一,负责产品的开发和交付,其重要性不言而喻。 那开发人员的职责和需要参加的活动是什么呢? Developers核心职责: 承诺并完…

【外汇天眼】不工作只做交易?探索不平凡的生活

金融界曾有一句广为流传的名言——"富人做债券,中产做股票,穷人做期货外汇"。这并非因为穷人特别热爱期货和外汇交易,而是因为他们更渴望通过杠杆交易在短期内实现财富梦想。如果一个人不愿意或没有技能去工作,但精通交…

Lambda 表达式使用详解,一篇文章手把手教会你

目录 1. Lambda 表达式有什么用? 2. 匿名内部类举例 3. Lambda 表达式的标准格式与使用 4. Lambda 表达式使用注意点 5. 什么是函数式接口? 6. Lambda 表达式的省略写法 7. Lambda 表达式省略写法简单展示 1. Lambda 表达式有什么用? …

LeetCode【394】字符串解码

题目&#xff1a; 思路&#xff1a; 参考&#xff1a;https://blog.csdn.net/xushiyu1996818/article/details/107973300 代码&#xff1a; public String decodeString(String s){Deque<Character> stack new ArrayDeque<>();for(char c :s.toCharArray()){if…

第一章:Android开发技能入门指南

Android 从入门到出门第一章&#xff1a;Android开发技能入门指南第二章&#xff1a;使用声明式UI创建屏幕并探索组合原则第三章&#xff1a;使用Hilt处理Jetpack Compose UI状态第四章&#xff1a;现代Android开发中的导航第五章&#xff1a;使用DataStore存储数据和测试第六章…

软考之系统架构师-01什么是系统架构师?什么是系统架构?

第一章 绪论 1. 什么是系统架构师&#xff1f; 系统架构设计师(System Architecture Designer&#xff09;是项目开发活动中的关键角色之一。系统架构是系统的一种整体的高层次的结构表示&#xff0c;是系统的骨架和根基&#xff0c;其决定了系统的健壮性和生命周期的长短。 …

系统架构师--面向对象选择题

系统架构师--面向对象选择题 面向对象考题&#xff08;将设计模式删减了&#xff0c;主要考察uml&#xff09;