-
课时73 Fragment shader - 纹理法线与凹凸贴图 1
-
课时74 Fragment shader - 纹理法线与凹凸贴图 2
1.灰度图范围0~1,高度图存放高度值。高度图每个像素可以是1或多个字节,一般用不到,可以从灰度图生成法线纹理(比如,将图标的TextureType改为Normalmap,然后勾选“Create from GrayScale”)。
2.需求:编写shader,从灰度图获取法线信息,而不是用上面提到的方法。
3.代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CreateNormalMap : MonoBehaviour {
//存放灰度图,格式为Default
//如果是普通彩色图,可以将(r+g+b)/3得到灰度图,但不是标准做法
public Texture2D tex0;
//存放生成的法线图,格式为Default
//由于要被写入,要求不能压缩,可设置为RGB 24 bit
//图片内容只是在Unity项目内临时做了修改,关闭项目后效果消失,建议做成工具,导出新贴图
public Texture2D tex1;
// Use this for initialization
void Start () {
for(int h=1;h<tex0.height-1;h++)//避免处理边缘像素点
for (int w=1;w<tex0.width-1;w++)
{
float uleft=tex0.GetPixel(w-1,h).r;//灰度图的每个分量值都一样
float uright=tex0.GetPixel(w+1,h).r;
float u=uright-uleft;
float vtop=tex0.GetPixel(w,h-1).r;
float vbottom=tex0.GetPixel(w,h+1).r;
float v=vbottom-vtop;
Vector3 vector_u=new Vector3(1,0,u);//构建x轴方向差分向量,叉积之前不需要规范化
Vector3 vector_v=new Vector3(0,1,v);//构建y轴方向差分向量
Vector3 N=Vector3.Cross(vector_u,vector_v).normalized;//每个分量范围-1~1
float r=N.x*0.5f+0.5f;
float g=N.y*0.5f+0.5f;
float b=N.z*0.5f+0.5f;
tex1.SetPixel(w,h,new Color(r,g,b));
}
tex1.Apply(false);
}
}
效果:
-
课时75 Fragment shader - 纹理法线与凹凸贴图 3
1.法线纹理贴图的解压方法UnpackNormal函数:dx5中,法线normal的x=>r=>w;y=>g=>y,z(b)由另外两个分量计算得出。所以法线贴图只用到第二(对应g)和第四(对应r)通道。
2.改进生成法线的代码:
3.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CreateNormalMapNew : MonoBehaviour {
//存放灰度图,格式为Default
//如果是普通彩色图,可以将(r+g+b)/3得到灰度图,但不是标准做法
public Texture2D tex0;
//存放生成的法线图,格式为Default
//由于要被写入,要求不能压缩,可设置为RGB 24 bit
//图片内容只是在Unity项目内临时做了修改,关闭项目后效果消失,建议做成工具,导出新贴图
public Texture2D tex1;
// Use this for initialization
void Start () {
for(int h=1;h<tex0.height-1;h++)//避免处理边缘像素点
for (int w=1;w<tex0.width-1;w++)
{
float uleft=tex0.GetPixel(w-1,h).r;//灰度图的每个分量值都一样
float uright=tex0.GetPixel(w+1,h).r;
float u=uright-uleft;
float vtop=tex0.GetPixel(w,h-1).r;
float vbottom=tex0.GetPixel(w,h+1).r;
float v=vbottom-vtop;
Vector3 vector_u=new Vector3(1,0,u);//构建x轴方向差分向量,叉积之前不需要规范化
Vector3 vector_v=new Vector3(0,1,v);//构建y轴方向差分向量
Vector3 N=Vector3.Cross(vector_u,vector_v).normalized;//法线信息存储于每个分量范围-1~1
float r=N.x*0.5f+0.5f;
float g=N.y*0.5f+0.5f;
float b=N.z*0.5f+0.5f;
tex1.SetPixel(w,h,new Color(r,g,b));//法线信息存储于rgb中,对应自建shader
// tex1.SetPixel(w,h,new Color(0,g,0,r));//法线信息存储于ga中,dx5中只用到yw分量,对应系统自带shader
}
tex1.Apply(false);
}
}
效果:
4.需求:如果法线贴图将法线信息存储在rgb中,实现自定义shader可以解压法线。
5.代码:
Shader "Custom/Lesson74"
{
Properties
{
_Color("Main Color",Color)=(1,1,1,1)
_MainTex("MainTex",2D)="white"{}
_BumpMap("NormalMap",2D)="bump"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 300
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
struct Input{
float2 uv_MainTex;
float2 uv_BumpMap;
};
void surf (Input IN,inout SurfaceOutput o)
{
fixed4 c=tex2D(_MainTex,IN.uv_MainTex)*_Color;
o.Albedo=c.rgb;
o.Alpha=c.a;
float4 cn=(tex2D(_BumpMap,IN.uv_BumpMap));
o.Normal=cn.rgb*2-1;//取代UnPackNormal方法
}
ENDCG
}
Fallback "Legacy Shaders/Diffuse"
}
-
课时76 Fragment shader - 纹理法线与凹凸贴图 4
1.纹理坐标系(切空间坐标系),由法线N,切线t和垂直于法线,切线的B向量组成。
2.需求:自定义Shader在片段程序内对法线贴图进行采样,并计算光照,在光照类型为点光源时也能显示正常。
3.当LightMode=ForwardBase,且场景只有一个点光源时,因为点光源ForwardBase下只能作为非像素光,所以法线计算显示有问题:
4.需要新建通道,LightMode=ForwardAdd。第一个通道可以渲染平行光和非像素光;第二个通道可以渲染平行光外的像素光;两个通道的混合模式为“blend one one”,否则第一个通道不起作用。
5.代码:
Shader "Custom/Lesson76"
{
//课时76 Fragment shader - 纹理法线与凹凸贴图 4
Properties{
_BumpTex("NormalMap", 2D)="white"{}
}
SubShader
{
Tags { "RenderType"="Geometry" }
pass{
tags{"LightMode"="ForwardBase"}//一个平行光,其余为非像素光
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "lighting.cginc"
struct v2f{
float4 pos:POSITION;
float2 uv:TEXCOORD0;
float wpos:TEXCOORD1;//世界空间坐标
float3 lightDir:TEXCOORD2;//切线空间光向量
};
sampler2D _BumpTex;
v2f vert(appdata_tan v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.wpos=mul(unity_ObjectToWorld,v.vertex);
o.uv=v.texcoord.xy;
//===================构建切线空间(相当于“TANGENT_SPACE_ROTATION;”)==========================
// float3 binormal=cross(v.tangent.xyz,v.normal);
// float3x3 rotation=float3x3(v.tangent.xyz,binormal,v.normal);
//=============================================
TANGENT_SPACE_ROTATION;//构建切线空间(通用方式)
o.lightDir=mul(rotation,_WorldSpaceLightPos0.xyz);//转换光向量至切线空间
return o;
}
//法线纹理采样要放在片段函数中
fixed4 frag(v2f IN):COLOR{
float3 L=normalize(IN.lightDir);
float3 N=UnpackNormal(tex2D(_BumpTex,IN.uv));//来自纹理贴图的法向量已在切线空间
N=normalize(N);
float ndotl=saturate(dot(N,L));
fixed4 col=_LightColor0*ndotl;
//非像素光
col.rgb+=Shade4PointLights(unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,
unity_LightColor[0].rgb,unity_LightColor[1].rgb,
unity_LightColor[2].rgb,unity_LightColor[3].rgb,
unity_4LightAtten0,
IN.wpos,N );
return col+UNITY_LIGHTMODEL_AMBIENT;
}
ENDCG
}
//=================================================
pass{
tags{"LightMode"="ForwardAdd"}//不止一个像素光
blend one one
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "lighting.cginc"
struct v2f{
float4 pos:POSITION;
float2 uv:TEXCOORD0;
float wpos:TEXCOORD1;//世界空间坐标
float3 lightDir:TEXCOORD2;//切线空间光向量
};
sampler2D _BumpTex;
v2f vert(appdata_tan v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.wpos=mul(unity_ObjectToWorld,v.vertex);
o.uv=v.texcoord.xy;
//===================构建切线空间(相当于“TANGENT_SPACE_ROTATION;”)==========================
// float3 binormal=cross(v.tangent.xyz,v.normal);
// float3x3 rotation=float3x3(v.tangent.xyz,binormal,v.normal);
//=============================================
TANGENT_SPACE_ROTATION;//构建切线空间(通用方式)
//_WorldSpaceLightPos0表示像素光,如果为平行光,记录方向信息,如果为点光源,记录位置信息
o.lightDir=mul(rotation,_WorldSpaceLightPos0.xyz);//转换光向量至切线空间
return o;
}
//法线纹理采样要放在片段函数中
fixed4 frag(v2f IN):COLOR{
float3 L=normalize(IN.lightDir);
float3 N=UnpackNormal(tex2D(_BumpTex,IN.uv));//来自纹理贴图的法向量已在切线空间
N=normalize(N);
float ndotl=saturate(dot(N,L));
float atten=1;
if(_WorldSpaceLightPos0.w!=0)//表示点光源,计算衰减.否则表示方向光,没有衰减
{
atten=1.0/length(IN.lightDir);
}
fixed4 col=_LightColor0*ndotl*atten;
return col+UNITY_LIGHTMODEL_AMBIENT;
}
ENDCG
}
}
Fallback "Diffuse"
}
效果: