unityshader用cubemap实现天空盒和环境映射(代码片段)

九九345 九九345     2023-04-06     722

关键词:

1 关于Cubemap

Cubemap在实时渲染中有很多应用,最常见的就是实现天空盒(Skybox)和环境映射(Environment Mapping)。

2 实现天空盒

2.1 实现原理

天空盒不陌生,而且一定还听说过天空球吧!因为实现天空盒的技术除了六面的立方体(cubemap),还可以用球面实现——这个时候天空盒就被叫做穹顶(Skydome)。因为这篇博客是cubemap专场,这里就重点讲一讲天空盒。

  • 是一个六面体封闭盒子纹理——这个盒子是由就像个展开的盒子(就像初中学几何展开图那样),可以理解成渲染除了一个巨大的六面体封闭盒子纹理,这六个面每个面的纹理都使用了cubemap立方体纹理映射技术;
  • 为了在游戏中模拟出符合实际的远处景观效果——当一个场景中使用了Skybox,摄像机移动的同时远处的景物纹理也跟着移动,这是为了模拟现实中“月亮走,我也走”、“永远走不到地平线尽头”这种现实中由于景物距离太远、太大带来的相对关系的错觉;
  • 它只是一个背景——另外,不得不提一点的是,如果只从按这个方法设置的Skybox,其实只是相对于摄像机的一个“背景”,至于一些光照的模拟,那还是要创建一个实实在在的Cubemap(下面会讲如何创建)。
  • 室内也有用到——天空盒不仅仅局限在“天空”了,现在例如教堂等宽敞的室内环境,也可以用Skybox来模拟;

此外,我还找到了一个20年发布的视频,以星际战甲游戏为例解释天空盒的:3分钟告诉你什么是天空盒?能把物品放大百倍的天空盒能用来做什么?warframe/星际战甲_哔哩哔哩_bilibili

以及知乎的一篇一步步学OpenGL(25) -《Skybox天空盒子》 - 知乎 (zhihu.com)

这两个可以一起辅助理解天空盒,同时也可以发现:现在常说的“天空盒”已经是实现纹理映射的天空盒了。

2.2 Unity中实现Skybox

首先跟其他任何材质一样,创建一个SkyboxMat材质也需要一个对应的Shader,Unity Shader自带的6 Sided Skybox满足了这个需求,需要导入6张创建立方体纹理需要的纹理,Wrap Mode选择为Clamp实现无缝拼接,就能得到如下效果:

如果想实现同一个场景中不同摄像机对应的Skybox不同,只需为当前摄像机创建一个新的Skybox组件就行! 

3 环境映射概述

参考自:环境映射技术漫谈 - 知乎 (zhihu.com)

IBL综述 - 知乎 (zhihu.com)

3.1 什么是环境映射? 

环境映射/环境纹理贴图(Environment Mapping, EM)也被称为反射映射(Reflection Mapping)。相当于设置一个虚拟的眼睛,生成一张关于周围环境的、虚拟的纹理图,然后把纹理图映射到模型上,此时模型表面的样子就是这张纹理图的一个景象,如果放置一个小球,就仿佛小球反射了周围场景的景象。

与光线追踪的关系

这就要谈及环境映射出来的效果的特点了。与其说是特点,不如说我们想用环境映射实现怎样的效果。通常环境映射是为了实现光滑物体表面反射形成周围环境影像的效果,这个效果是属于环境光的。

而实现这一效果除了环境映射这一技术还有光线追踪(Ray Tracing, 又或者说全局光照,Global Illumination, GI),但如果用光线追踪仅仅为了实现这个小目的就显得有些大材小用了,因此实时渲染中更多的还是选择环境映射技术来实现。

3.2 什么是IBL?

环境贴图是基于IBL(Image Based Lighting, 基于图像光照)从而实现的,了解IBL也很有必要。

如何理解“基于图像”?

有了之前的天空盒的学习,很容易理解“基于图像”是指基于那6张按照特定顺序排列的纹理。IBL通常结合cubemap,我们在进行学习、使用时会拿到一张现成的cubemap,通常一张cubemap采集自真实的照片或者从3D场景中生成,由6张按规则排序的纹理图片组成。IBL相当于一个无限大的球面光源在照射场景,每张图片的每个像素都会被当作一个光源进行后续的光照计算。

Cubemap与球体图

这里的图像,除了本博客主要涉及的cubemap立方体图以外, 还可以是球体图(Equirectangular map),类似世界地图的那种。当然,无论是立方体还是球体,区别不大,坐标可以相互转换的,二者对比如下图(图源IBL综述 - 知乎 (zhihu.com)),上为交叉布局的Cubemap,下为Cubemap转换过来的全景布局的球体图:

参考文章还提出了:一般立方体图用的比较多,即使导入的是球体图也会转换成立方体图,立方体渲染比球体简单很多,同时立方体也会更加节省宽带(一些光照计算很少用到的无效位置会进行压缩)。

3.3 环境映射的步骤

参考自环境映射技术漫谈 - 知乎 (zhihu.com)

前面提到了,环境映射就相当于把整个环境信息“贴”到一个表面上,这个表面可以是球也可以是一个立方体,通俗来讲实现它有以下三个步骤:

  • 将相机放在场景中心,获取环境图像
  • 将图像“贴”在球/立方体表面上
  • 绘制物体表面的镜面效果时,直接从上述图像纹理采样获得对应的纹素

更细致的算法实现步骤为:

  • 创建环境贴图
  • 计算顶点法向量n和顶点到视点的向量v
  • 根据n和v计算反射向量r
  • 根据反射向量r与贴图纹理坐标的映射关系,计算纹理坐标(u, v)
  • 用(u, v)采样环境贴图对应的纹素

3.4 几种环境映射技术

根据上面的算法步骤可以发现,我们还需要额外确定一个东西——反射向量r与贴图纹理坐标的映射关系,而这个映射关系就涉及到了几种不同的环境映射技术,关于这一点可以直接看环境映射技术漫谈 - 知乎 (zhihu.com)本篇博客就不再赘述。

通过了本章节的概述,相信对环境映射已经有了一个大概的了解,接下来就正式进入使用Cubemap实现环境映射的具体步骤。

4 创建Cubemap

在Unity中创建Cubemap最常用两种方法,指路我的另一篇博客,这里就不赘述了:【Unity Shader】Unity中如何创建Cubemap?

5 环境映射:反射效果

使用了反射效果之后,物体就是一个镜面反射的状态,像镀了一层金属。

5.1 实现代码

Shader "Unity Shaders Book/Chapter 10/Reflection"

    Properties 
        _Color ("Color", Color) = (1, 1, 1, 1)
        _ReflectColor ("Reflect Color", Color) = (1, 1, 1, 1)
        _ReflectAmount ("Reflect Amount", range(0, 1)) = 1
        _Cubemap ("Reflect Cubemap", Cube) = "_Skybox" 
    

    SubShader 
        Pass 
            Tags  "lightMode"="ForwardBase" 

            CGPROGRAM

            #pragma multi_compile_fwdbase

            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            //Properties
            fixed4 _Color;
            fixed4 _ReflectColor;
            fixed _ReflectAmount;
            samplerCUBE _Cubemap;
            
            struct a2v 
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            ;

            struct v2f 
                float4 pos : SV_POSITION;
                float3 worldPos : TEXCOORD0;
                fixed3 worldNormal : TEXCOORD1;
                fixed3 worldViewDir : TEXCOORD2;
                fixed3 worldLightDir : TEXCOORD3;
                fixed3 worldReflect : TEXCOORD5;
                SHADOW_COORDS(4)
            ;

            v2f vert(a2v v) 
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal).xyz;
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos).xyz;
                o.worldLightDir = UnityWorldSpaceLightDir(o.worldPos).xyz;
                o.worldReflect = reflect(-o.worldViewDir, o.worldNormal);

                TRANSFER_SHADOW(o);

                return o;
            

            fixed4 frag(v2f i) : SV_Target 
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldViewDir = normalize(i.worldViewDir);
                fixed3 worldLightDir = normalize(i.worldLightDir);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 diffuse = _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal, worldLightDir));
                //用反射方向采样cubemap
                fixed3 reflect = texCUBE(_Cubemap, i.worldReflect).rgb * _ReflectColor.rgb;

                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

                fixed3 color = ambient + lerp(diffuse, reflect, _ReflectAmount) * atten;
                return fixed4(color, 1.0);

            
            ENDCG
        
    
    FallBack  "Reflective/VertexLit"

5.2 效果

5.3 一些要点

如何去写Shader就不需要赘述了,我们重点关注于代码中出现的新朋友:

Cube对应的变量 

首先,关注于Shader中使用Cubemap用到的不同于之前2D纹理的"Cube"和"samplerCUBE":

_Cubemap ("Reflect Cubemap", Cube) = "_Skybox" 

...

samplerCUBE _Cubemap;

texCUBE

接着直接来到如何计算反射部分:

fixed3 reflect = texCUBE(_Cubemap, i.worldReflect).rgb * _ReflectColor.rgb;

用到了texCUBE,用法可以直接类比tex2D,只不过前者需要用一个反射方向去采样,后者直接用uv坐标采样。

反射方向worldRflect

代码中并没有对反射方向worldRflect归一化,这是因为texCUBE使用的矢量只需要表示出方向即可,不需要归一化参与到其他的计算中。

变量_ReflectAmount

你可能还注意到,代码中定义了一个_ReflectAmount变量,如何使用的呢?

fixed3 color = ambient + lerp(diffuse, reflect, _ReflectAmount) * atten;

看上去是为了将漫反射颜色diffuse和反射颜色reflect中和一下。

等一下?漫反射和反射?这有没有让你想起来什么?——没错!菲涅尔项(games101中学到过)啊!菲涅尔项就是为了模拟反射处反射光和折射(漫反射)光强之间的比值变化,关于菲涅尔反射后续会涉及到,这里我估计只是用了一个简单的值来插值,模拟二者过度。 

6 环境映射:折射效果

除了反射肯定还有折射,提到折射一定能想到斯涅耳定律(Snell's Law),老朋友了!具体定律我早在101的作业中就学习过,这里不赘述:GAMES101作业5-从头到尾理解代码&Whitted光线追踪

6.1 什么物体需要考虑折射?

我认为与其是扒清楚斯涅尔定律的内部原理(原理分析指路一篇我觉得写得还不错的BRDF学习系列(一) 光的反射与折射 - 知乎 (zhihu.com)),作为使用者来说我们更需要知道在什么情况下需要考虑到折射:当光线从透明物体穿越到另一个透明物体时,就会发生折射(传播方向发生改变)。也就是说!如果一个物体我们想要他是透明材质,例如玻璃等,就需要考虑折射,此时环境映射就需要实现折射效果。

6.2 实现代码

Shader "Unity Shaders Book/Chapter 10/Refraction"

    Properties 
        _Color ("Color", Color) = (1, 1, 1, 1)
        _RefractColor ("Refract Color", Color) = (1, 1, 1, 1)
        _RefractAmount ("Refract Amount", range(0, 1)) = 1
        _RefractRatio ("Refract Rastio", range(0.1, 1)) = 0.5
        _Cubemap ("Refract Cubemap", Cube) = "_Skybox" 
    

    SubShader 
        Pass 
            Tags  "lightMode"="ForwardBase" 

            CGPROGRAM

            #pragma multi_compile_fwdbase

            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            //Properties
            fixed4 _Color;
            fixed4 _RefractColor;
            fixed _RefractAmount;
            fixed _RefractRatio;
            samplerCUBE _Cubemap;
            
            struct a2v 
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            ;

            struct v2f 
                float4 pos : SV_POSITION;
                float3 worldPos : TEXCOORD0;
                fixed3 worldNormal : TEXCOORD1;
                fixed3 worldViewDir : TEXCOORD2;
                fixed3 worldLightDir : TEXCOORD3;
                fixed3 worldRefract : TEXCOORD5;
                SHADOW_COORDS(4)
            ;

            v2f vert(a2v v) 
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal).xyz;
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos).xyz;
                o.worldLightDir = UnityWorldSpaceLightDir(o.worldPos).xyz;
                o.worldRefract = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);

                TRANSFER_SHADOW(o);

                return o;
            

            fixed4 frag(v2f i) : SV_Target 
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldViewDir = normalize(i.worldViewDir);
                fixed3 worldLightDir = normalize(i.worldLightDir);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 diffuse = _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal, worldLightDir));
                //用折射采样cubemap
                fixed3 refract = texCUBE(_Cubemap, i.worldRefract).rgb * _RefractColor.rgb;

                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

                fixed3 color = ambient + lerp(diffuse, refract, _RefractAmount) * atten;
                return fixed4(color, 1.0);

            
            ENDCG
        
    
    FallBack  "Reflective/VertexLit"

6.3 效果

6.4 一些要点

refract(i, n, ri)

fixed3 refract = texCUBE(_Cubemap, i.worldRefract).rgb * _RefractColor.rgb;

代码与反射相比更加复杂一点点,但复杂就复杂在用到的计算函数refract需要输入三个参数:refract(i, n, ri),其中除了入射向量i、法向量n外还有入射和折射介质折射率的比值ri,i和n输入的都必须是归一化后的向量

_RefractRatio

这个参数就是上面提到的输入的第三个参数,入射和折射光线所在介质的折射率之间的比值。但空气折射率接近1.0,其余的透明介质的折射率几乎都大于1.0,所以设置值时range(0.1, 1)

7 环境映射:菲涅尔反射

关于菲涅尔定律,可以直接看看Everything has Fresnel – Filmic Worlds,有举出现实生活中例如砖块、衣服布料等材质物体上出现的菲涅尔效果。我在GAMES101作业5-从头到尾理解代码&Whitted光线追踪中也对菲涅尔效应做了简单的介绍。

真实的菲涅尔效应很复杂(也是101作业用的那个原式子),实时渲染中通常用Schlick菲涅尔近似等式来计算:

其中, —— 指当光线垂直反射时,反射占的比例,我们可以用来控制菲涅尔反射的整体强度;其他的参数就不多说了。

如果想了解更详细可以参考:BRDF学习系列(二) 漫反射、高光反射、菲涅尔反射 - 知乎 (zhihu.com)

7.1 实现代码

Shader "Unity Shaders Book/Chapter 10/Fresnel"

    Properties 
        _Color ("Color", Color) = (1, 1, 1, 1)
        _FresnelScale ("Fresnel Scale", range(0, 1)) = 0.5
        _Cubemap ("Reflect Cubemap", Cube) = "_Skybox" 
    

    SubShader 
        Pass 
            Tags  "lightMode"="ForwardBase" 

            CGPROGRAM

            #pragma multi_compile_fwdbase

            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            //Properties
            fixed4 _Color;
            fixed _FresnelScale;
            samplerCUBE _Cubemap;
            
            struct a2v 
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            ;

            struct v2f 
                float4 pos : SV_POSITION;
                float3 worldPos : TEXCOORD0;
                fixed3 worldNormal : TEXCOORD1;
                fixed3 worldViewDir : TEXCOORD2;
                fixed3 worldLightDir : TEXCOORD3;
                fixed3 worldReflect : TEXCOORD5;
                SHADOW_COORDS(4)
            ;

            v2f vert(a2v v) 
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal).xyz;
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos).xyz;
                o.worldLightDir = UnityWorldSpaceLightDir(o.worldPos).xyz;
                o.worldReflect = reflect(-o.worldViewDir, o.worldNormal);

                TRANSFER_SHADOW(o);

                return o;
            

            fixed4 frag(v2f i) : SV_Target 
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldViewDir = normalize(i.worldViewDir);
                fixed3 worldLightDir = normalize(i.worldLightDir);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 diffuse = _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal, worldLightDir));
                //用反射方向采样cubemap
                fixed3 reflect = texCUBE(_Cubemap, i.worldReflect).rgb;

                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
                fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow((1 - dot(worldViewDir, worldNormal)), 5);

                fixed3 color = ambient + lerp(diffuse, reflect, saturate(fresnel)) * atten;
                return fixed4(color, 1.0);

            
            ENDCG
        
    
    FallBack  "Reflective/VertexLit"

7.2 效果

展示的是当前FresnelScale=0的效果,是个具有边缘光效果的漫反射物体(场景中有一个方向光):

这是因为,当FresnelScale=0时,lerp(diffuse, reflection, saturate(fresnel))=pow((1 - dot(worldViewDir, worldNormal)), 5),这个值是几乎接近diffuse的,也就是着色完全由环境光+漫反射颜色代替了,因此本体几乎是接近是漫反射的效果。

7.3 为什么有边缘光效果?

我们需要知道!这种边缘光效果是无论fresnelScale取值多少都有的。

其原因也很好理解,当接近物体边缘时,视线方向viewDir和模型边缘表面顶点的法线n是几乎接近垂直的,也就意味着dot(worldViewDir, worldNormal)值接近0 -> fresnel的值接近1 -> lerp(diffuse, reflection, saturate(fresnel))=reflection,这意味着!边缘处的着色始终都会是几乎接近reflection的。

opengl学习脚印:立方体纹理和天空包围盒(cubemapsandskybox)

...纹理有待我们进一步学习,本节将要学习的立方体纹理(cubemaps),是一种将多个纹理图片复合到一个立方体表面的技术。在游戏中应用得较多的天空包围盒可以使用cubemap实现。本节示例程序均可以在我的github下载。本节内容整理... 查看详情

shaderlab学习小结(十八)cubemap

现在还没用到cubemap(除了天空盒子),只是初步的学了一下最简单的cubemap的shader找一幅图好吧,有点大,在unity里把它设置成cubemapOK,图有了,看shader,代码挺简单的,其实和普通的2D贴图shader挺像Shader"Custom/Cubemap"{Properties{_Cube... 查看详情

第二人生的源码分析12天空显示的实现

在虚拟世界里,自然现象的实现是最需要实现的,比如天空的实现,以便反映是白天还是晚上,这样才逼真反映现实世界。在第二人生里实现的天空,还是比较好的,如下图所示:蔡军生 2008/01/10QQ:9073204深圳从上面的图片里... 查看详情

unityshader实现网格划分

1. 2.有的时候,大家可能会遇到这种需求:显示某个物体的线框,就像汽车设计图纸(CAD之类的)那样。例如下面这种效果:效果1:效果2:  用shader就可以解决这个问题。甚至可以不写代码,去资源商店下载即可。比... 查看详情

天空图立方体贴图转化为辐照度立方体贴图(代码片段)

...体贴图的大小决定被转化的辐照度贴图的精度。irradianceCubeMap=newCubeMap(32);调用CubeMap::CubeMap(intCubeSize):CubeSize(CubeSize)initializeOpenGLFunctions();//CubeMap创建空间图glGenTextures(1,&envCubemap);glBindTexture(GL_TEXTURE_CUBE_MAP,envCubemap);for(unsignedint... 查看详情

unityshader--实现翻页效果

在unity中我们可以通过shader来实现各种有意思而且炫酷的一些渲染效果出来。比如流光效果,毛玻璃效果,平静水面,波涛汹涌的海面,甚至是一些炫酷的技能特效等。主要就是因为unity为我们渲染准备了一个shaderlab的结构,我... 查看详情

unityshader入门教程反射光斑的实现

本节内容介绍PhongModel(也就是上文说的反射光的计算模型),采用的计算方法是BlinPhong(也即是用视线方向V+光源方向L的和,与N做点积,随后幂化得到高光反射系数)下图采用了csdn博文http://blog.csdn.net/u010133610/article/details/522066... 查看详情

用sublimetext当unityshader的编辑器

用VisualStudio写shader实在蛋疼,那可能就会有人要问了,为啥不用插件可视化制作shader呢?因为我是新手,新手还是老老实实敲代码,慢慢来… 所以试着在网上找找,有没有类似的插件或者编辑器,被我找到了,也基本符合我... 查看详情

unityshader效果学习

Unity上对于图像的处理,假设单纯使用代码。那么非常遗憾,程序基本会跑死,毕竟是直接对像素的操作,读取写入都是比較耗费CPU和内存的。所以。这次由于项目须要想实现类似哈哈镜的效果。想来想去,还是认为用unity的... 查看详情

unityshader------uv动画原理及简易实现(代码片段)

...的版本,避免一些因为版本不一致而出现的问题。 【UnityShader】(三)------光照模型原理及漫反射和高光反射的实现【UnityShader】(四)------纹理之法线纹理、单张纹理及遮罩纹理的实现【UnityShader】(五)------透明效果之半... 查看详情

Java 沙盒和 ProcessBuilder

...:2018-07-0413:40:17【问题描述】:我按照tutorial为插件代码实现了java沙盒。插件代码使用以下权限运行:privatePermissionCollectionpluginPermissions()Permissionspermissions=newPermissions();per 查看详情

unityshader——置灰(代码片段)

实现置灰非常简单,只要用原来颜色的rgb分量按照不同的权重加起来,就可以得到一个灰度值,这个灰度值就是新的颜色的rgb分量。Shader"WS/Gray" Properties _MainTex("Texture",2D)="white" SubShader Tags"RenderType&qu... 查看详情

unityshader------高级纹理之渲染纹理及镜子与玻璃效果的实现(代码片段)

...相近的版本,避免一些因为版本不一致而出现的问题。【UnityShader】(三)------光照模型原理及漫反射和高光反射的实现【UnityShader】(四)------纹理之法线纹理、单张纹理及遮罩纹理的实现【UnityShader】(五)------透明效果之半... 查看详情

unityshader纹理映射的实现

1//纹理映射的编写2Shader"TMoon/04-Texture"{3Properties{4_Color("Color",Color)=(1,1,1,1)5_MainTex("MainTex",2D)="white"{}6}78SubShader{910Pass{1112Tags{"LightMode"="ForwardBase"}1314CGPROGRAM1516#include"Ligh 查看详情

unity中控制天空盒移动的解决办法(代码片段)

...我的工程中会报错(我的unity版本5.5.5f1):我的天空盒是Cubemap型的,挂在MainCamera上,因此修改代码如下:privatevoidUpdate()RotateSkybox();//控制天空盒旋转privatevoidRotateSkybox()/*floatnum=RenderSettings.skybox.GetFloat("_Rotation");RenderSettings.skybox.S... 查看详情

环境映射(environmentmapping)的立方映射

...上前后左右上下六个贴图来模拟天空、宇宙等环境,称为Cubemap有的引擎中成为Skybox,立方环境映射的原理就是在游戏中所需要产生映射的物体的位置动态生成一套Cubemap,再对Cubemap进行采样生成物体表面应该反射出的周遭环境。... 查看详情

unityshader入门精要学习笔记-第17章unity的表面着色器探秘

转自冯乐乐的《UnityShader入门精要》2010年的Unity3中,SurfaceShader出现了。表面着色器的一个例子。我们先做如下准备工作。1)新建一个场景,去掉天空盒子2)新建一个材质,新建一个Shader,赋给材质。3)场景中创建一个胶囊体... 查看详情

unityshader------透明效果之半透明效果的实现及原理(代码片段)

...018相近的版本,避免一些因为版本不一致而出现的问题【UnityShader学习笔记】(三)----------------光照模型原理及漫反射和高光反射的实现【UnityShader】(四)------纹理之法线纹理、单张纹理及遮罩纹理的实现 前言相信读者对... 查看详情