百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

Unity Shaders学习笔记--SurfaceShader(九)Cubemap

bigegpt 2025-05-02 09:10 8 浏览

【Unity Shaders】学习笔记——SurfaceShader(九)Cubemap

  1. 如果你想从零开始学习Unity Shader,那么你可以看看本系列的文章入门,你只需要稍微有点编程的概念就可以。

  2. 水平有限,难免有谬误之处,望指出。

上一节中讲述了制作Cubemap的方法。这一节讲讲怎么使用它。

Simple Cubemap

先来看一下最简单的Cubemap。

Shader "Custom/SimpleReflection" 
{
    Properties 
    {
        _MainTint ("Diffuse Tint", Color) = (1,1,1,1)
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _Cubemap ("CubeMap", CUBE) = ""{}
        _ReflAmount ("Reflection Amount", Range(0.01, 1)) = 0.5
    }
    
    SubShader 
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf Lambert

        sampler2D _MainTex;
        samplerCUBE _Cubemap;
        float4 _MainTint;
        float _ReflAmount;

        struct Input 
        {
 float2 uv_MainTex;
 float3 worldRefl;
        };

        void surf (Input IN, inout SurfaceOutput o) 
        {
 half4 c = tex2D (_MainTex, IN.uv_MainTex) * _MainTint;
 o.Emission = texCUBE(_Cubemap, IN.worldRefl).rgb * _ReflAmount;
 o.Albedo = c.rgb;
 o.Alpha = c.a;
        }
        ENDCG
    } 
    FallBack "Diffuse"
}

其实就是用texCUBE函数来对Cubemap采样。

第二个参数应该传入UV坐标,但实际传入的是世界反射向量。这是Unity替我们计算了UV坐标。

Cubemap是由六张贴图构成的,组成了一个类似天空盒的六面体,那这六张贴图是如何映射到小球上的呢,使小球看起来像倒映着周围的环境一样?

将Cubemap想象成一个立方体,包裹着小球,从小球的中心点发射一条射线,穿过小球表面和立方体表面,射线与小球和立方体会各有一个交点,小球上的这个点对应的就是立方体上的这个点的纹理。那要怎样计算各个点对应的UV坐标呢?从小球中心点发射的一条条射线其实就是法线,有个很明显的事实就是,法线朝向法线坐标分量最大的坐标轴指向的立方体的面。也就是坐标(1,1,3)的法线朝向的是Z轴指向的立方体的面。这样就由法线找到了点对应的面。那么UV坐标又该如何计算呢?法线另外两个较小的坐标值和UV坐标是有关系的。举个特殊点的例子,小球最顶点的法线的坐标应该是(0,0,1),对应的应该是立方体的顶面的中点,UV坐标应该是(0.5,0.5)。计算UV坐标的方法是(x/z×0.5+0.5,y/z×0.5+0.5),将特殊点代进去,答案是正确的。原理有点难说明,相当于把X、Y坐标投影到了对应的面上。乘0.5加0.5是为了让法线坐标的区间从[-1,1]变为[0,1]。

在Unity里,我们不必自己计算,可以直接使用内置变量worldRefl来检索Cubemap。

Normal Cubemap

Shader "Custom/NormalMappedReflection" 
{
    Properties 
    {
        _MainTint ("Diffuse Tint", Color) = (1,1,1,1)
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _NormalMap ("Normal Map", 2D) = "bump" {}
        _Cubemap ("Cubemap", CUBE) = ""{}
        _ReflAmount ("Reflection Amount", Range(0,1)) = 0.5
    }
    
    SubShader
     {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf Lambert

        samplerCUBE _Cubemap;
        sampler2D _MainTex;
        sampler2D _NormalMap;
        float4 _MainTint;
        float _ReflAmount;

        struct Input 
        {
 float2 uv_MainTex;
 float2 uv_NormalMap;
 float3 worldRefl;
 INTERNAL_DATA
        };

        void surf (Input IN, inout SurfaceOutput o) 
        {
 half4 c = tex2D (_MainTex, IN.uv_MainTex);
 
 float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)).rgb;
 o.Normal = normals;
 
 o.Emission = texCUBE (_Cubemap, WorldReflectionVector (IN, o.Normal)).rgb * _ReflAmount;
 o.Albedo = c.rgb * _MainTint;
 o.Alpha = c.a;
        }
        ENDCG
    } 
    FallBack "Diffuse"
}

就只是增加了法线贴图而已。 因为法线信息的改变,所以要重新计算传入texCUBE函数的世界反射向量。float3 worldRefl;INTERNAL_DATA变量用于原本的法线信息不使用时,比如使用了法线贴图,原来的法线信息就不使用了。要根据新的法线信息计算世界反射向量使用WorldReflectionVector函数。

动态立方图系统

有时候游戏里的物体从一个环境走到另一个环境,需要更换Cubemap,这样显示的反射效果才真实。

有两种方法更换Cubemap,一种是实时更换Cubemap,这样的效果最真实,但是要牺牲性能;第二种是当物体走到另一个环境的时候更换Cubemap,这就是我要讲的方法。

方法很简单,就是在C#里用SetTexture的方法动态更换Cubemap。

[ExecuteInEditMode]
public class SwapCubemaps : MonoBehaviour 
{
    public Cubemap cubeA;
    public Cubemap cubeB;
    
    public Transform posA;
    public Transform posB;
    
    private Material curMat;
    private Cubemap curCube;
    

    // Use this for initialization
    void Start  
    {
    
    }
    
    // Update is called once per frame
    void Update  
    {
        curMat = renderer.sharedMaterial;
        if(curMat)
        {
 curCube = CheckProbeDistance;
 curMat.SetTexture("_Cubemap", curCube);
 
        }
    }
    
    private Cubemap CheckProbeDistance
    {
        float distA = Vector3.Distance(transform.position, posA.position);
        float distB = Vector3.Distance(transform.position, posB.position);
        
        if(distA < distB)
        {
 return cubeA;
        }
        else if(distB < distA)
        {
 return cubeB;
        }
        else
        {
 return cubeA;
        }
        
    }
        
    
    void OnDrawGizmos
    {
        Gizmos.color = Color.green;
        
        if(posA)
        {
 Gizmos.DrawWireSphere(posA.position, 0.5f);
        }
        
        if(posB)
        {
 Gizmos.DrawWireSphere(posB.position, 0.5f);
        }
    }
}
  1. [ExecuteInEditMode]是为了让脚本在编辑器状态的时候也能执行,这样就不必点击Play调试,比较方便。

  2. OnDrawGizmos是在Scene里画一些可视化的东西方便调试。

  3. CheckProbeDistance里用Distance判断物体和A点、B点的距离,根据距离决定返回哪种Cubemap。

  4. 在Update设置材质的Cubemap。

Cubemap效果

Cubemap

我有加个金属的纹理。效果是这样的。

NormalCubemap

这是加了法线贴图的。

相关推荐

当Frida来“敲”门(frida是什么)

0x1渗透测试瓶颈目前,碰到越来越多的大客户都会将核心资产业务集中在统一的APP上,或者对自己比较重要的APP,如自己的主业务,办公APP进行加壳,流量加密,投入了很多精力在移动端的防护上。而现在挖...

服务端性能测试实战3-性能测试脚本开发

前言在前面的两篇文章中,我们分别介绍了性能测试的理论知识以及性能测试计划制定,本篇文章将重点介绍性能测试脚本开发。脚本开发将分为两个阶段:阶段一:了解各个接口的入参、出参,使用Python代码模拟前端...

Springboot整合Apache Ftpserver拓展功能及业务讲解(三)

今日分享每天分享技术实战干货,技术在于积累和收藏,希望可以帮助到您,同时也希望获得您的支持和关注。架构开源地址:https://gitee.com/msxyspringboot整合Ftpserver参...

Linux和Windows下:Python Crypto模块安装方式区别

一、Linux环境下:fromCrypto.SignatureimportPKCS1_v1_5如果导包报错:ImportError:Nomodulenamed'Crypt...

Python 3 加密简介(python des加密解密)

Python3的标准库中是没多少用来解决加密的,不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍,但重点会放在两个第三方的软件包:PyCrypto和cryptography上,我...

怎样从零开始编译一个魔兽世界开源服务端Windows

第二章:编译和安装我是艾西,上期我们讲述到编译一个魔兽世界开源服务端环境准备,那么今天跟大家聊聊怎么编译和安装我们直接进入正题(上一章没有看到的小伙伴可以点我主页查看)编译服务端:在D盘新建一个文件夹...

附1-Conda部署安装及基本使用(conda安装教程)

Windows环境安装安装介质下载下载地址:https://www.anaconda.com/products/individual安装Anaconda安装时,选择自定义安装,选择自定义安装路径:配置...

如何配置全世界最小的 MySQL 服务器

配置全世界最小的MySQL服务器——如何在一块IntelEdison为控制板上安装一个MySQL服务器。介绍在我最近的一篇博文中,物联网,消息以及MySQL,我展示了如果Partic...

如何使用Github Action来自动化编译PolarDB-PG数据库

随着PolarDB在国产数据库领域荣膺桂冠并持续获得广泛认可,越来越多的学生和技术爱好者开始关注并涉足这款由阿里巴巴集团倾力打造且性能卓越的关系型云原生数据库。有很多同学想要上手尝试,却卡在了编译数据...

面向NDK开发者的Android 7.0变更(ndk android.mk)

订阅Google官方微信公众号:谷歌开发者。与谷歌一起创造未来!受Android平台其他改进的影响,为了方便加载本机代码,AndroidM和N中的动态链接器对编写整洁且跨平台兼容的本机...

信创改造--人大金仓(Kingbase)数据库安装、备份恢复的问题纪要

问题一:在安装KingbaseES时,安装用户对于安装路径需有“读”、“写”、“执行”的权限。在Linux系统中,需要以非root用户执行安装程序,且该用户要有标准的home目录,您可...

OpenSSH 安全漏洞,修补操作一手掌握

1.漏洞概述近日,国家信息安全漏洞库(CNNVD)收到关于OpenSSH安全漏洞(CNNVD-202407-017、CVE-2024-6387)情况的报送。攻击者可以利用该漏洞在无需认证的情况下,通...

Linux:lsof命令详解(linux lsof命令详解)

介绍欢迎来到这篇博客。在这篇博客中,我们将学习Unix/Linux系统上的lsof命令行工具。命令行工具是您使用CLI(命令行界面)而不是GUI(图形用户界面)运行的程序或工具。lsoflsof代表&...

幻隐说固态第一期:固态硬盘接口类别

前排声明所有信息来源于网络收集,如有错误请评论区指出更正。废话不多说,目前固态硬盘接口按速度由慢到快分有这几类:SATA、mSATA、SATAExpress、PCI-E、m.2、u.2。下面我们来...

新品轰炸 影驰SSD多款产品登Computex

分享泡泡网SSD固态硬盘频道6月6日台北电脑展作为全球第二、亚洲最大的3C/IT产业链专业展,吸引了众多IT厂商和全球各地媒体的热烈关注,全球存储新势力—影驰,也积极参与其中,为广大玩家朋友带来了...