Unity shader学习---基础纹理

一.Unity的纹理概念

纹理最基础的目的:用一张图片控制模型的外观。使用纹理映射(texture mapping)技术,我们可以把一张图“粘”在模型表面,逐纹素(texel)(名字是为了和逐像素进行区分)的控制模型的颜色。
建模软件中利用纹理展开技术把纹理映射坐标存储在每个顶点上。如下图

纹理映射坐标(UV坐标,u为横向坐标,v为纵向坐标)定义了该顶点在纹理中对应的2D坐标

UV坐标会被归一化到[0, 1]范围内。
OpenGL和DirectX在二维纹理空间的坐标有差异。OpenGL原点位于左下角,DirectX位于左上角,Unity默认OpenGL模式。

二.单张纹理.FilterMode属性

A.单张纹理应用

通常使用一张纹理来代替物体的漫反射颜色
纹理名_ST表示声明某个纹理的属性,name##_ST.xy 存储缩放值,name##_ST.zw存储偏移值。
结合之前的光照模型的单张纹理应用:

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
Shader "Single Texture" {
    Properties {
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader {     
        Pass { 
            Tags { "LightMode"="ForwardBase" }
        
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            
            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Specular;
            float _Gloss;
            
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };
            
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };
            
            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                
                o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                // Or just call the built-in function
//              o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                
                // Use the texture to sample the diffuse color
                fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
                
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
                
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
                
                return fixed4(ambient + diffuse + specular, 1.0);
            }
            
            ENDCG
        }
    } 
    FallBack "Specular"
}

要点1:tex2D函数表示对纹理进行采样,param1是需要被采样的纹理,param2是float2类型的纹理坐标,返回计算得到的纹素值。
要点2:TRASFORM_TEX实在UnityCG.cginc中定义的:

//Transform 2D UV by scale/bias property
//param1是顶点的纹理坐标
//param2是纹理的名称
#define TRANSFORM_TEX(tex,name)(tex.xy * name##_ST.xy + name##_ST.zw)

效果如下图:


B.纹理的属性

为了阐释纹理的属性,编写一个更为纯粹的纹理属性应用shader

Shader "TextureProperties" {
    Properties {
        _MainTex ("Main Tex", 2D) = "white" {}
    }
    SubShader {
        Pass { 
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;

            struct a2v {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
            };
            
            struct v2f {
                float4 position : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            v2f vert(a2v v) {
                v2f o;
                // Transform the vertex from object space to projection space
                o.position = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }
            fixed4 frag(v2f i) : SV_Target {
                fixed4 c = tex2D(_MainTex, i.uv);
                return fixed4(c.rgb, 1.0);
            }
            ENDCG
        }
    } 
    FallBack "Diffuse"
}

用这个shader生成一个纹理图如下:


1.Wrap Mode属性

决定纹理坐标超过[0,1]范围后使用哪种平铺方式。
a.Repeat——重复纹理采样。
这种模式下,如果纹理的坐标超过了1,那么整数部分将会被舍弃,而直接使用小数部分进行采用。
b.Clamp模式时,超过的部分将会截取到边界值,从而形成一个条形结构。如下图:


2.FilterMode属性

决定了当纹理由于变换而产生拉伸时会使用哪种滤波模式。
三种模式:Point模式Bilinear滤波Trilinear滤波
a.Point模式使用了最近邻滤波,在放大或缩小时,它的采样像素数目通常只有一个,因此图像看起来有种像素风格的效果。
b.Bilinear滤波采用了线性滤波,对于每个目标像素,它会找4个邻近的像素,然后对他们进行线性插值混合后得到最终的像素,因此图像看起来像被模糊了。
c.Trilinear滤波几乎和Bilinear是一样的,只是Trilinear还会在多级渐远纹理(mipmapping)之间进行混合。
效果如下:

三.凹凸映射

目的:使用一张纹理来修改模型表面的发现。
方法:1.高度映射 : 使用高度纹理来模拟表面位移,后得到修改后的法线值。
2.法线映射:使用一张法线纹理直接存储表面法线。

1.法线映射

法线纹理中存储的就是表面的法线方向,由于法线的方向的分量范围在[-1,1],而像素的分量范围为[0,1],因此我们需要做一个映射:
pixel =0.5*normal + 0.5
实际使用:normal = pixel * 2 - 1

模型空间法线纹理:对于模型自带的表面法线,修改后存储在此纹理中。
切线空间法线纹理:对于模型的每个定点都有一个属于自己的切线空间,这个切线空间的原点就是该定点的本身,而z轴是定点的法线方向,x轴是切线方向,而y轴有法线和切线的叉积得到,也被称为是副切线或副法线。

模型顶点的切线空间。原点对应了顶点坐标,x轴:切线空间(t),y轴是副切线方向(b),z轴是法线方向(n)

左边:模型空间下的法线纹理,右边:切线空间下的法线纹理

左边的模型空间法线之所以是五颜六色的,因为所有法线所在坐标空间是在模型空间,x/y/z区间都在0到1之间,所以能显示彩色。
切线空间下的法线纹理几乎是浅蓝色,因为,每个法线方向所在的坐标空间是不一样的,即是表面各点各自的切线空间。这种法线纹理其实就是存储了每个点在各自的切线空间中的法线扰动方向。

2.实践代码

由于法线纹理存储的法线是切线空间下的方向,有两种选择:
1.在切线空间下计算光照,需要把光照方向、视角方向变换到切线空间。
2.都在世界空间进行光照计算。
下面是选择世界空间下计算光照模型的代码。

Shader "NormalMapWorldSpace" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap ("BumpMap",2D) = "white"{}
        _BumpScale("BumpScale",Range(-2.0,2.0)) = 1.0
        _Specular("Specular",Color)=(1,1,1,1)
        _Diffuse("Diffuse",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(8,256)) = 40
    }
    SubShader {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            fixed4 _Color;
            sampler2D _MainTex; float4 _MainTex_ST;
            sampler2D _BumpMap; float4 _BumpMap_ST;
            float _BumpScale;
            fixed4 _Specular;
            fixed4 _Diffuse;
            float _Gloss;
            struct a2v
            {
                float4 vertex:POSITION;
                float3 normal:NORMAL;
                float4 texcoord:TEXCOORD0;
                float4 tangent:TANGENT;
            };
            struct v2f
            {
                float4 pos:SV_POSITION;
                float4 TtoW0:TEXCOORD0;
                float4 TtoW1:TEXCOORD1;
                float4 TtoW2:TEXCOORD2;
                float4 uv:TEXCOORD3;
            };
            v2f vert(a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
              
                float3 worldPos = mul(_Object2World,v.vertex).xyz;
                float3 worldNormal = mul(v.normal,(float3x3)_World2Object);
                //这里要怎样计算?有两种方式
                float3 worldTangent = UnityObjectToWorldDir(v.tangent);
                //float3 worldTangent = mul((float3x3)_Object2World,v.tagent);
                //这里要怎样计算?
                //叉积求得
                float3 worldBionormal = cross(worldNormal,worldTangent) * v.tangent.w;
                //计算了世界空间下顶点切线、副切线和法线的表示,并把它们按列摆放得到从切线空间到世界空间的变换矩阵
                //把该矩阵的每一行分别存放在TtoW0、TtoW1、TtoW2中
                //最后把世界空间下顶点位置x、y、z分量分别存储在这些变量的w分量中
                o.TtoW0 = float4(worldTangent.x,worldBionormal.x,worldNormal.x,worldPos.x);
                o.TtoW1 = float4(worldTangent.y,worldBionormal.y,worldNormal.y,worldPos.y);
                o.TtoW2 = float4(worldTangent.z,worldBionormal.z,worldNormal.z,worldPos.z);
                //o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                //o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
                o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
                o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpMap);
               
                return o;
            }
           
            fixed4 frag(v2f i):SV_Target
           {
                //i.uv.xy
                fixed3 albedo = tex2D(_MainTex,i.uv).rgb * _Color.rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
                float3 worldPos = float3(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w);
                float3 worldViewDir =normalize( UnityWorldSpaceViewDir(worldPos) );
                float3 worldLightDir =normalize( UnityWorldSpaceLightDir(worldPos) );
                //不需要这个值了,所有法线信息都是用bumpMap中的!!!包括漫反射的计算!
                float3 worldNormal = normalize( float3(i.TtoW0.z,i.TtoW1.z,i.TtoW2.z) );
                //UnpackNormal对法线纹理进行采样和解码(需要把法线纹理标识成Normal map)
                fixed3 bump = UnpackNormal(tex2D(_BumpMap,i.uv.zw));
                //倘若_BumpScale为0,那么bump=float3(0,0,1)就相当于入射表面没有任何扰动
                bump.xy *= _BumpScale;
                //需要保证经过处理的法线仍然是归一化的单位向量
                //dot计算时,对应的分量相乘然后相加,dot(bump.xy,bump.xy)=x*x + y*y
                bump.z = sqrt(1.0- saturate( dot(bump.xy,bump.xy) ));
                //将法线从切线空间转换到世界空间,这里是在模拟bump左乘【切线空间到世界空间的转换矩阵】
                //我潜意识中两种种错误的写法
                //bump = normalize(float3(bump.x * i.TtoW0.xyz,bump.y*i.TtoW1.xyz,bump.z*i.TtoW2.xyz));
                //bump = normalize(float3(i.TtoW0.xyz * bump,i.TtoW1.xyz * bump,
                //正确写法,原来,单个向量默认相当于列向量
                bump = normalize(half3(dot(i.TtoW0.xyz,bump),dot(i.TtoW1.xyz,bump),dot(i.TtoW2.xyz,bump)));
                //计算漫发射时同样适用BumpMap中的法线信息!
                fixed3 diffuse = _LightColor0.rgb * albedo* saturate(dot(bump,worldLightDir));
                float3 halfDir = normalize(worldViewDir + worldLightDir);
                //一定千万要注意,前面废了好大的劲,就是为了在这一步计算高光时能用上!!!
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(bump,halfDir)),_Gloss);
                return fixed4(ambient + diffuse + specular, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

基本思路:
由于我们只能在片元着色器中获得bumpMap的纹素信息,所以我们需要在片元着色器中使用切线空间到世界空间的转换矩阵。首先在顶点着色器中获得切线空间各个坐标轴在世界空间中的表示,副切线副通过转换好的worldNormal与worldTangent的叉积获得,注意方向靠v.tangent的w分量来确定。在v2f结构体声明了TtoW0、TtoW1、TtoW2三个纹理寄存器,用来分别将存放转换矩阵的每一行。由于需要在片元着色器用到worldPos来计算worldViewDir以及worldLightDir,所以需要向片元着色器传递该信息,传递方式为将该变量的三个分量分别存放到TtoW0、TtoW1、TtoW2的w分量中。
效果如下图


_BumpScale为-0.8

四.渐变纹理

之前计算漫反射光照,都是使用表面法线和光照方向的点积结果与材质的反射率相乘。渐变纹理是使用一张渐变纹理来控制漫反射光照。

Shader "Ramp Texture" {
    Properties {
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        _RampTex ("Ramp Tex", 2D) = "white" {}
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader {
        Pass { 
            Tags { "LightMode"="ForwardBase" }
        
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            
            fixed4 _Color;
            sampler2D _RampTex;
            float4 _RampTex_ST;
            fixed4 _Specular;
            float _Gloss;
            
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };
            
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float2 uv : TEXCOORD2;
            };
            
            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                
                o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
                
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                
                // Use the texture to sample the diffuse color
                fixed halfLambert  = 0.5 * dot(worldNormal, worldLightDir) + 0.5;
                fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;
                
                fixed3 diffuse = _LightColor0.rgb * diffuseColor;
                
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
                
                return fixed4(ambient + diffuse + specular, 1.0);
            }
            
            ENDCG
        }
    } 
    FallBack "Specular"
}

利用半兰伯特构建float2变量,以此对一张渐变纹理进行采样。漫反射的颜色靠此方式获得。
效果图:


使用不同的渐变纹理来控制漫反射光照

五.遮罩纹理

使用遮罩纹理的流程:通过采样得到的遮罩纹理的纹素值,然后使用其中某个(或某几个)通道的值(如texel.r)来与某种表面属性进行相乘,这样,当该通道的值为0时,可以保护表面不受该属性的影响。
遮罩纹理的使用是对高光效果进行遮罩,使用同一组uv坐标,对同一位置处的遮罩纹理进行采样,这里只使用了r分量。
世界空间下高光遮罩纹理实现:

Shader "MaskTexWorld" {
    Properties {
        _Color ("ColorTint", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Gloss ("Gloss", Range(8,256)) = 20
        _BumpMap("BumpMap",2D) = "white"{}
        _BumpScale("BumpScale",Range(-1,1)) = 1
        _Specular("Specular",Color) = (1,1,1,1)
        _SpecularMask("SpecularMask",2D) = "white"{}//高光反射遮罩纹理
        _SpecularScale("SpecularScale",float) = 1.0//控制遮罩影响度的系数
    }
    SubShader {
        Pass
        {
            Tags{"LgihtMode" = "ForwarBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Gloss;
            sampler2D _BumpMap;
            float _BumpScale;
            fixed4 _Specular;
            sampler2D _SpecularMask;
            float _SpecularScale;
            struct a2v
            {
                float4 vertex:POSITION;
                float3 normal:NORMAL;
               //注意因为我们要使用tangent的w分量,所以这里的类型是float4
                float4 texcoord:TEXCOORD0;
                float4 tangent:TANGENT;
            };
            struct v2f
            {
                float4 pos:SV_POSITION;
                float4 TtoW0:TEXCOORD0;
                float4 TtoW1:TEXCOORD1;
                float4 TtoW2:TEXCOORD2;
                float2 uv:TEXCOORD3;
            };
            v2f vert(a2v v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
                
                float3 worldNormal = normalize( UnityObjectToWorldNormal(v.normal) );
                float3 worldTangent = normalize( UnityObjectToWorldDir(v.tangent.xyz) );
                float3 worldBionormal = cross(worldNormal,worldTangent)*v.tangent.w;
                float3 worldPos = mul(_Object2World,v.vertex);
                o.TtoW0 = float4(worldTangent.x,worldBionormal.x,worldNormal.x,worldPos.x);
                o.TtoW1 = float4(worldTangent.y,worldBionormal.y,worldNormal.y,worldPos.y);
                o.TtoW2 = float4(worldTangent.z,worldBionormal.z,worldNormal.z,worldPos.z);
                o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                return o;
            }
            fixed4 frag(v2f i):SV_Target
            {
                float3 worldPos = float3(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w);
                
                float3 worldLightDir =normalize(UnityWorldSpaceLightDir(worldPos));
                
                float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                float3 bump = UnpackNormal(tex2D(_BumpMap,i.uv));
                bump.xy *= _BumpScale;
                bump.z = sqrt(1-saturate(dot(bump.xy,bump.xy)));
                float3 worldNormal = normalize( float3(dot(bump,i.TtoW0.xyz),dot(bump,i.TtoW1.xyz),dot(bump,i.TtoW2.xyz)) );
                fixed3 albedo = tex2D(_MainTex,i.uv).rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal,worldLightDir));  
                //通过uv对MaskTex对应位置进行采样
                //所谓的 specularMask 实际上只利用采样纹理的一个分量通道,这里仅仅利用r
                fixed specularMask = tex2D(_SpecularMask,i.uv).r * _SpecularScale;
                fixed3 halfDir = normalize(worldLightDir + worldViewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal,halfDir)),_Gloss) * specularMask;
                return fixed4(ambient + diffuse + specular,1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

效果如下


左边使用了遮罩纹理,右边没有
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 存储在每个顶点上。纹理映射坐标定义了该顶点在纹理中对应的 2D 坐标。通常,这些坐标使用一个二维变量(u, v)来...
    李偌闲阅读 804评论 0 1
  • 转载自VR设计云课堂[https://www.jianshu.com/u/c7ffdc4b379e]Unity S...
    水月凡阅读 1,006评论 0 0
  • 相关概念 纹理 用一张图片控制模型的外观具体的做法是将一张图“黏”在模型表面,逐纹素控制模型的颜色 纹理映射坐标 ...
    全新的饭阅读 1,240评论 0 0
  • 一个三维场景的画面的好坏,百分之四十取决于模型,百分之六十取决于贴图,可见贴图在画面中所占的重要性。在这里我将列举...
    CXYMichael阅读 1,524评论 2 5
  • 毛星云网名「浅墨」,微软MVP,《Windows游戏编程之从零开始》作者 本文将总结提炼“GPU精粹三部曲“11本...
    猫叔WS阅读 2,479评论 0 3