前几天看了两个shader分别是聚光灯和法线贴图,于是想把这两个shader结合起来,产生手电照射潮湿的凹凸墙面效果:

本想很容易就能实现但是由于之前不理解光照模型的计算原理,所以我改起来相当费劲,经过努力终于弄明白了两者的原理,然后写出了shader:

struct Mtrl
{
 float4 ambient;
 float4 diffuse;
 float4 spec;
 float  specPower;
};

struct DirLight
{
 float4 ambient;
 float4 diffuse;
 float4 spec;
 float3 posW;
 float3 dirW; 
};

uniform extern float4x4 gWorldInv;
uniform extern float4x4 gWVP;
uniform extern float4x4 gWorld;     //世界矩阵
uniform extern Mtrl     gMtrl;
uniform extern DirLight gLight;
uniform extern float3   gEyePosW;
uniform extern texture  gTex;
uniform extern texture  gNormalMap;
uniform extern float3 gAttenuation012;  //光源衰减参数
uniform extern float  gSpotPower;    //光源聚合度,越大聚合越小

sampler TexS = sampler_state
{
 Texture = <gTex>;
 MinFilter = ANISOTROPIC;
 MaxAnisotropy = 8;
 MagFilter = LINEAR;
 MipFilter = LINEAR;
 AddressU  = WRAP;
    AddressV  = WRAP;
};

sampler NormalMapS = sampler_state
{
 Texture = <gNormalMap>;
 MinFilter = ANISOTROPIC;
 MaxAnisotropy = 8;
 MagFilter = LINEAR;
 MipFilter = LINEAR;
 AddressU  = WRAP;
    AddressV  = WRAP;
};
 
struct OutputVS
{
    float4 posH      : POSITION0;
    float3 toEyeT    : TEXCOORD0;
    float3 lightDirT : TEXCOORD1;
    float2 tex0      : TEXCOORD2;
    float4 posW      : TEXCOORD3;
};

OutputVS NormalMapVS(float3 posL      : POSITION0,
                     float3 tangentL  : TANGENT0,
                     float3 binormalL : BINORMAL0,
                     float3 normalL   : NORMAL0,
                     float2 tex0      : TEXCOORD0)
{
    // 初始化输出结构体对象
 OutputVS outVS = (OutputVS)0;
 
 //创建TBN坐标系,用来计算法线贴图
 float3x3 TBN;
 TBN[0] = tangentL;
 TBN[1] = binormalL;
 TBN[2] = normalL;
 
 // 将顶点渲染器输入的参数 传入到TBN坐标系中
 float3x3 toTangentSpace = transpose(TBN);
 
 // 将摄像机的位置转换到本地坐标系中,只需乘上世界矩阵的转置逆矩阵就可以了
 float3 eyePosL = mul(float4(gEyePosW, 1.0f), gWorldInv);
 
 // 计算从顶点到摄像机的方向向量  并转换到TBN坐标系中
 float3 toEyeL = eyePosL - posL;
 outVS.toEyeT = mul(toEyeL, toTangentSpace);
 
 // 将灯光的方向转换到本地坐标系后,再转换到TBN坐标系中
 float3 lightDirL = mul(float4(gLight.dirW, 0.0f), gWorldInv).xyz;
 outVS.lightDirT  = mul(lightDirL, toTangentSpace);
 
 // posH为经过世界、观察、投影等坐标转换的最终可以直接渲染的顶点位置,其他的则转换到世界坐标系中
 outVS.posH = mul(float4(posL, 1.0f), gWVP);
 outVS.posW = mul(float4(posL, 1.0f), gWorld);
 gLight.posW = mul(float4(gLight.posW, 1.0f), gWorld);   //将灯光坐标转换到世界坐标系
 gLight.dirW = mul(float4(gLight.dirW, 1.0f), gWorld);   //将灯方向转换到世界坐标系
 // 放大uv贴图的坐标,因为该图片使用了WRAP寻址模式,所以放大的效果是加倍纹理的uv平铺
 outVS.tex0 = tex0*3.f;
 
 // 返回输出结构体,然后将里面的内容传入到下面的像素渲染器里继续处理!
    return outVS;
}

float4 NormalMapPS(float3 toEyeT    : TEXCOORD0,
                   float3 lightDirT : TEXCOORD1,
                   float2 tex0      : TEXCOORD2,
                   float4 posW   :TEXCOORD3) : COLOR
{
 // 单位化
 toEyeT    = normalize(toEyeT);
 lightDirT = normalize(lightDirT);
 
 // 灯光向量即为灯光方向的相反
 float3 lightVecT = -lightDirT;
 
 // 对法线纹理进行采样
 float3 normalT = tex2D(NormalMapS, tex0);
 
 // 将法线参数范围从[0, 1] 转换到[-1, 1] 
    normalT = 2.0f*normalT - 1.0f;
    
 //单位化 
 normalT = normalize(normalT);
 
 // 计算镜面光向量
 float3 r = reflect(-lightVecT, normalT);
 
 // 计算将有多少镜面光射入到人眼中
 float t  = pow(max(dot(r, toEyeT), 0.0f), gMtrl.specPower);
 
 // 通过灯光向量和法线向量的夹角来决定漫反射光的强度
 float s = max(dot(lightVecT, normalT), 0.0f);
 
 // 如果漫反射强度过低则将镜面光置零
 if(s <= 0.0f)
      t = 0.0f;
 
 // 计算三个系数 spec diffuse 随时变化 ambient 保证物体总是有一定的亮度
 float3 spec = t*(gMtrl.spec*gLight.spec).rgb;
 float3 diffuse = s*(gMtrl.diffuse*gLight.diffuse).rgb;
 float3 ambient = gMtrl.ambient*gLight.ambient;
 
 // 对原纹理进行采样
 float4 texColor = tex2D(TexS, tex0);
 
 //计算锥光源衰减参数
 float d = distance(gLight.posW, posW.xyz);
 float A = gAttenuation012.x + gAttenuation012.y*d + gAttenuation012.z*d*d;
 float3 lightVector = normalize(gLight.posW - posW.xyz);
 gLight.dirW = normalize(gLight.dirW);
 // 计算聚光灯系数
 float spot = pow(max(dot(-lightVector, gLight.dirW), 0.0f), gSpotPower);

//最终颜色

float3 color = spot*2.f*( ambient*texColor.rgb +diffuse*texColor.rgb /A+spec); 
 // 输出颜色
    return float4(color, gMtrl.diffuse.a*texColor.a);
}

technique NormalMapTech
{
    pass P0
    {
        vertexShader = compile vs_2_0 NormalMapVS();
        pixelShader  = compile ps_2_0 NormalMapPS();
    }
}

一定要搞清各坐标系中的计算 不要弄混。对于shader的调试也很重要,SDK中可以做到,也可以借助renderMonkey,但是我还不熟,有空研究一下,这工具会非常的方便。

聚光灯+法线贴图 shader相关推荐

  1. 【游戏开发进阶】带你玩转模型法线,实验一下大胆的想法(法线贴图 | shader | Unity | python | 爬虫)

    文章目录 一.前言 二.直观感受法线贴图 三.表面法线 1.表面法线的概念 2.空间与坐标系 2.1.世界空间--世界坐标系 2.2.局部空间--局部坐标系 2.3.切线空间--切线坐标系 2.4.小 ...

  2. Unity Shader法线贴图(Normal Map)及其原理

    简介 以前经常听说"模型不好看啊,怎么办啊?"答曰"加法线","做了个高模,准备烘一下法线贴图","有的美术特别屌,直接画法线贴图 ...

  3. unity基础学习之法线贴图

    原理和概念 法线是一个向量(x,y,z),每一个顶点都有一个法线,用一个纹理去存储的话,那就是(r,g,b),由于法线是垂直于一个面的,对于2d图片来说,那他的z值就是1 用一张纹理来存储法线的值,法 ...

  4. OpenGL shader normals法线贴图的实例

    OpenGL shader normals法线贴图 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <glad/glad.h> #in ...

  5. shader 获取法线_Unity Shader 入门到改行5——法线贴图

    the best of blur 1. 法线贴图理论 1.1 什么是法线贴图 一般的贴图中存储的是表面颜色值(RGBA),而法线贴图存放的则是法线信息(xyzw),假设某顶点处的 uv 坐标为 (u, ...

  6. UV滚动+自定义光线高光+LightMap阴影贴图+法线贴图效果shader

    Shader "Custom/FireRingUVShader" {Properties {_Color ("Color Tint", Color) = (1, ...

  7. Unity Shader - 切线空间的法线贴图应用(T2W W2T)

    法线贴图 法线贴图(或是法线纹理)其实就是一张图片中的RGB通道分别存储着法线方向的纹理(有些为了数据压缩将X,Y存储在RG通道,Z是通过1-dot(xy,xy)来近似计算). 它的由来是因为高模运行 ...

  8. shader graph_在Shader Graph中使用表面梯度框架进行法线贴图合成

    shader graph A recent Unity Labs paper introduces a new framework for blending normal maps that is e ...

  9. OpenGL.Shader:9-学习光照-法线贴图(计算TBN矩阵)

    OpenGL.Shader:9-学习光照-法线贴图(计算TBN矩阵) 这次文章学习法线贴图,法线贴图在游戏开发和GIS系统开发当中尤为广泛,其表现力特别的强,绘制的效果特别接近真实.更重要的一点就是, ...

最新文章

  1. docker 漏洞测试 靶机环境 靶机平台 vulhub vulapps 简介
  2. 安装python后如何打开-Python的安装与使用
  3. 全球及中国交联的高密度聚乙烯行业投资应用与供应需求规模分析报告2022版
  4. 关于mpi的理论知识以及编写程序来实现数据积分中的梯形积分法。
  5. 大数据可视化软件//完美的SCI配图
  6. DataUml Design 教程6-DataUML Design 1.1版本号正式公布(支持PD数据模型)
  7. 计算机操作系统思维导图_我在b站学计算机
  8. 后端:Spring IOC 知识点总结,写得太好了!
  9. redis 类型、方法
  10. 贪心算法与动态规划的区别与联系
  11. 04_(终结版)通过App实现对数据库的增删改
  12. python js 性能_Python Json使用,Json库性能测试
  13. electron 使用json作为本地存储_使用腾讯云对象存储 COS 作为 Velero 后端存储,实现集群资源备份和还原...
  14. 动态规划01背包问题入门学习,详细笔记,推荐阅读
  15. 中科大自主招生计算机,中科大自主招生(中科大自主招生试题)
  16. 同时打开多个独立Excel窗口
  17. Python 爬取新浪网新闻和存取CSV文件
  18. CDH Parcel包starrocks集成cloudera Manager
  19. [pillow]透明图片和不透明图片叠加合成--添加透明度
  20. 三种方式修改 MySQL 数据库名

热门文章

  1. 从零开始使用ZBrush制作同人版Ellie游戏角色模型,这个教程不要错过!
  2. Spring如何引入其他资源:<import>和@Import @ImportResource
  3. mysql中关于group的语句指令_关于group by 以下语句正确的是( )_学小易找答案...
  4. 在前端对Excel文件进行下载
  5. 【SRP协议】The Secure Remote Password Protocol论文笔记
  6. 夜光带你走进JavaScript(四十六)擅长的领域
  7. [狂神说JAVA] 再小的帆也能远航
  8. u盘启动蓝屏 索尼vaio_索尼vaio笔记本电脑怎么设置USB启动
  9. 孙子算经余数C语言,2020国考行测备考:探索《孙子算经》之剩余定理
  10. 删除avast相关文件