Skip to content
  • 首页
  • 留言
  • 关于

Share the joys of programming and technology

OpenGL-法线贴图(Normal Mapping)

OpenGL-法线贴图(Normal Mapping)

2022年2月3日 liyanliang Comments 0 Comment
阅读次数: 419

背景

在一个平面上如果只有一个法线,整个面的光照强度都是相同的,如果是凹凸的表面,无法表现表面的真是光照效果,所以引入了法线贴图。平面内的每一个fragment都存储一个法线向量,通过法线向量计算切线向量和副切向向量。

已知上向量是表面的法线向量。右和前向量是切线(Tagent)和副切线(Bitangent)向量。下面的图片展示了一个表面的三个向量:

计算原理

计算每个表面的切线和副切线可以参照https://learnopengl-cn.github.io/,主要计算过程如下:

顶点:

         V3N3UV2  verts[] =
      {
          { 0.5f, -0.5f,  0.5f ,  1.0, 0.0, 0.0, 0.0f, 0.0f },
          { 0.5f, -0.5f, -0.5f ,  1.0, 0.0, 0.0, 1.0f, 0.0f },
          { 0.5f,  0.5f, -0.5f ,  1.0, 0.0, 0.0, 1.0f, 1.0f },
                                                             
          { 0.5f,  0.5f, -0.5f ,  1.0, 0.0, 0.0, 1.0f, 1.0f },
          { 0.5f,  0.5f,  0.5f ,  1.0, 0.0, 0.0, 0.0f, 1.0f },
          { 0.5f, -0.5f,  0.5f ,  1.0, 0.0, 0.0, 0.0f, 0.0f },
                                                             
          {-0.5f, -0.5f, -0.5f ,  -1.0, 0.0,0.0, 0.0f, 0.0f },
          {-0.5f, -0.5f,  0.5f ,  -1.0, 0.0,0.0, 1.0f, 0.0f },
          {-0.5f,  0.5f,  0.5f ,  -1.0, 0.0,0.0, 1.0f, 1.0f },
                                                             
          {-0.5f,  0.5f,  0.5f ,  -1.0, 0.0,0.0, 1.0f, 1.0f },
          {-0.5f,  0.5f, -0.5f ,  -1.0, 0.0,0.0, 0.0f, 1.0f },
          {-0.5f, -0.5f, -0.5f ,  -1.0, 0.0,0.0, 0.0f, 0.0f },
                                                             
          {-0.5f,  0.5f,  0.5f ,  -0.0, 1.0,0.0, 1.0f, 0.0f },
          { 0.5f,  0.5f,  0.5f ,  -0.0, 1.0,0.0, 0.0f, 0.0f },
          { 0.5f,  0.5f, -0.5f ,  -0.0, 1.0,0.0, 0.0f, 1.0f },
​
          { 0.5f,  0.5f, -0.5f ,  0.0, 1.0,0.0, 0.0f, 1.0f   },
          {-0.5f,  0.5f, -0.5f ,  0.0, 1.0,0.0, 1.0f, 1.0f   },
          {-0.5f,  0.5f,  0.5f ,  0.0, 1.0,0.0, 1.0f, 0.0f   },
​
          {-0.5f, -0.5f, -0.5f ,  0.0, -1.0,0.0, 1.0f, 0.0f },
          { 0.5f, -0.5f, -0.5f ,  0.0, -1.0,0.0, 0.0f, 0.0f },
          { 0.5f, -0.5f,  0.5f ,  0.0, -1.0,0.0, 0.0f, 1.0f },
                                                             
          { 0.5f, -0.5f,  0.5f ,  0.0, -1.0,0.0, 0.0f, 1.0f },
          {-0.5f, -0.5f,  0.5f ,  0.0, -1.0,0.0, 1.0f, 1.0f },
          {-0.5f, -0.5f, -0.5f ,  0.0, -1.0,0.0, 1.0f, 0.0f },
​
          {-0.5f, -0.5f,  0.5f,   0.0, 0.0, 1.0,  0.0f, 0.0f },
          { 0.5f, -0.5f,  0.5f,   0.0, 0.0, 1.0,  1.0f, 0.0f },
          { 0.5f,  0.5f,  0.5f,   0.0, 0.0, 1.0,  1.0f, 1.0f },
                                                             
          { 0.5f,  0.5f,  0.5f,   0.0, 0.0, 1.0,  1.0f, 1.0f },
          {-0.5f,  0.5f,  0.5f,   0.0, 0.0, 1.0,  0.0f, 1.0f },
          {-0.5f, -0.5f,  0.5f,   0.0, 0.0, 1.0,  0.0f, 0.0f },
                                                             
          { 0.5f, -0.5f, -0.5f,   0.0, 0.0,-1.0,  0.0f, 0.0f },
          {-0.5f, -0.5f, -0.5f,   0.0, 0.0,-1.0,  1.0f, 0.0f },
          {-0.5f,  0.5f, -0.5f,   0.0, 0.0,-1.0,  1.0f, 1.0f },
                                                             
          {-0.5f,  0.5f, -0.5f,   0.0, 0.0,-1.0,  1.0f, 1.0 },
          { 0.5f,  0.5f, -0.5f,   0.0, 0.0,-1.0,  0.0f, 1.0 },
          { 0.5f, -0.5f, -0.5f,   0.0, 0.0,-1.0,  0.0f, 0.0 },
      };

切线与副切线计算:

     void convertTBN(V3N3UV2* vertices,NORMALMAP* nmVerts)
  {
       for (size_t i = 0; i <36; i += 3)
      {
           nmVerts[i + 0].x = vertices[i + 0].x;
           nmVerts[i + 0].y = vertices[i + 0].y;
           nmVerts[i + 0].z = vertices[i + 0].z;
​
           nmVerts[i + 1].x = vertices[i + 1].x;
           nmVerts[i + 1].y = vertices[i + 1].y;
           nmVerts[i + 1].z = vertices[i + 1].z;
​
           nmVerts[i + 2].x = vertices[i + 2].x;
           nmVerts[i + 2].y = vertices[i + 2].y;
           nmVerts[i + 2].z = vertices[i + 2].z;
​
           nmVerts[i + 0].nx = vertices[i + 0].nx;
           nmVerts[i + 0].ny = vertices[i + 0].ny;
           nmVerts[i + 0].nz = vertices[i + 0].nz;
​
           nmVerts[i + 1].nx = vertices[i + 1].nx;
           nmVerts[i + 1].ny = vertices[i + 1].ny;
           nmVerts[i + 1].nz = vertices[i + 1].nz;
​
           nmVerts[i + 2].nx = vertices[i + 2].nx;
           nmVerts[i + 2].ny = vertices[i + 2].ny;
           nmVerts[i + 2].nz = vertices[i + 2].nz;
​
           nmVerts[i + 0].u = vertices[i + 0].u;
           nmVerts[i + 0].v = vertices[i + 0].v;
​
           nmVerts[i + 1].u = vertices[i + 1].u;
           nmVerts[i + 1].v = vertices[i + 1].v;
​
           nmVerts[i + 2].u = vertices[i + 2].u;
           nmVerts[i + 2].v = vertices[i + 2].v;
​
​
           // Shortcuts for vertices
           float3  v0  =   float3(vertices[i + 0].x,vertices[i + 0].y,vertices[i + 0].z);
           float3  v1  =   float3(vertices[i + 1].x,vertices[i + 1].y,vertices[i + 1].z);
           float3  v2  =   float3(vertices[i + 2].x,vertices[i + 2].y,vertices[i + 2].z);
​
           float2  uv0 =   float2(vertices[i + 0].u, vertices[i + 0].v);
           float2  uv1 =   float2(vertices[i + 1].u, vertices[i + 1].v);
           float2  uv2 =   float2(vertices[i + 2].u, vertices[i + 2].v);
           
           // Edges of the triangle : postion delta
           float3  deltaPos1   =   v1 - v0;
           float3  deltaPos2   =   v2 - v0;
           // UV delta
           float2 deltaUV1     =   uv1 - uv0;
           float2 deltaUV2     =   uv2 - uv0;
​
           float   r           =   1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
           float3 tangent      =   (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y)*r;
           float3 binormal     =   (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x)*r;
​
           nmVerts[i + 0].tx = tangent.x;  nmVerts[i + 1].tx = tangent.x;  nmVerts[i + 2].tx = tangent.x;
           nmVerts[i + 0].ty = tangent.y;  nmVerts[i + 1].ty = tangent.y;  nmVerts[i + 2].ty = tangent.y;
           nmVerts[i + 0].tz = tangent.z;  nmVerts[i + 1].tz = tangent.z;  nmVerts[i + 2].tz = tangent.z;
​
           nmVerts[i + 0].bx = binormal.x; nmVerts[i + 1].bx = binormal.x; nmVerts[i + 2].bx = binormal.x;
           nmVerts[i + 0].by = binormal.y; nmVerts[i + 1].by = binormal.y; nmVerts[i + 2].by = binormal.y;
           nmVerts[i + 0].bz = binormal.z; nmVerts[i + 1].bz = binormal.z; nmVerts[i + 2].bz = binormal.z;
      }
  }

顶点着色器:

         const char* vs  =   "#version 330 \n\
                           in     vec3   _position;\n\
                           in     vec3   _normal;\n\
                           in     vec2   _uv;\n\
                           in     vec3   _tagent;\n\
                           in     vec3   _biTagent;\n\
                           out     vec2   _outUV;\n\
                           out     vec3   _outPos;\n\
                           out     mat3   _TBN;\n\
                           uniform mat4   _mvp;\n\
                           uniform mat4   _matWorld;\n\
                           uniform mat3   _normalMatrix;; \n\
                           uniform vec3   _lightPos;\n\
                           uniform vec3   _cameraPos;\n\
                           void main()\n\
                           {\n\
                               _outUV         =   _uv;\n\
                               vec4   pos     =   _matWorld*vec4(_position,1);\n\
                               mat3   norMapT =   _normalMatrix;\n\
                               _outPos         =   pos.xyz;\n\
                               vec3   normal     =   normalize(norMapT * _normal);\n\
                               vec3   tagent     =   normalize(norMapT * _tagent);\n\
                               vec3   biTagent   =   normalize(norMapT * _biTagent);\n\
                               _TBN               =   mat3x3(tagent,biTagent,normal);\n\
                               gl_Position         =   _mvp * vec4(_position,1.0);\n\
                           }";

片段着色器:

      const char* ps =    "#version 330 \n\
                           in     vec2       _outUV;\n\
                           in     vec3       _outPos;\n\
                           in     mat3       _TBN;\n\
                           uniform sampler2D   _texture;\n\
                           uniform sampler2D   _texNormal;\n\
                           uniform vec3       _lightPos;\n\
                           uniform vec3       _cameraPos;\n\
                           void main()\n\
                           {\n\
                               gl_FragColor.rgb   =   texture2D(_texture, _outUV).rgb;\n\
                               vec3   normal     =   normalize((_TBN * (texture2D(_texNormal, _outUV).rgb * 2.0 - 1.0)));\n\
                               vec3   lightDir   =   normalize(_lightPos - _outPos);\n\
                               vec3   cameraDir   =   normalize(_cameraPos - _outPos);\n\
                               float   NdotLD     =   max(dot(normal, lightDir), 0.0);\n\
                               gl_FragColor.rgb   *= 0.2 + 0.8 * NdotLD;\n\
                               vec3   lightDirRef =   reflect(-lightDir, normal);\n\
                               float   CDdotLDR   =   max(dot(cameraDir, lightDirRef), 0.0);\n\
                               gl_FragColor.rgb   += pow(CDdotLDR, 64.0);\n\
                           }";

完整项目代码:

https://github.com/mc-liyanliang/OpenGL-Shader/tree/master

相关文章

  • OpenGL绘制桥梁模型OpenGL绘制桥梁模型
  • 两跨连续梁影响线绘制-附源码两跨连续梁影响线绘制-附源码
  • 调用另外一个项目中的函数-设计模式解耦调用另外一个项目中的函数-设计模式解耦
  • C++实现一个简单的语言解释器C++实现一个简单的语言解释器
  • 提取最小封闭区域提取最小封闭区域
  • 如何调试Revit二次开发代码-教学视频(解决无法调试问题)如何调试Revit二次开发代码-教学视频(解决无法调试问题)

OpenGL
NormalMapping

Post navigation

PREVIOUS
OpenGL-卡通着色(Cartoon)
NEXT
C++实现一个简单的语言解释器

发表回复 取消回复

您的邮箱地址不会被公开。 必填项已用 * 标注

近期文章

  • ANR崩溃日志查看方法
  • 通过数学方法来计算short类型的变量w的低八位x和高八位
  • 3dTiles数据解析
  • Games101和Games202脑图汇总
  • LearnOpenGL脑图汇总
  • IBL计算总结
  • C++实现一个简单的语言解释器
  • OpenGL-法线贴图(Normal Mapping)
  • OpenGL-卡通着色(Cartoon)
  • OpenGL几何着色器实现贝塞尔曲线
  • WinDbg检查内存泄漏
  • OpenGL雾化效果实现-每像素雾化
  • OpenGL实现billboard效果(CPU)
  • 算法:寻找异常数字
  • OpenGL 几何着色器的应用
  • Midas XD-构件详图开发
  • Midas XD-选筋助手开发
  • Civil Designer开发-检测规范自动生成控制截面
  • Civil Designer开发-公路桥梁承载能力检算评定
  • Midas W-满堂支架快速建模助手开发

全站热点

  • C++编写的情人节小程序 (2,083)
  • 提取最小封闭区域 (1,696)
  • Modern OpenGL绘制圆柱体 (1,607)
  • OpenGL开发环境搭建-GLFW与GLAD配置 超详细 (1,465)
  • 截面特性计算程序-附源码 (1,290)
  • OpenGL绘制旋转立方体 (1,110)
  • 判断一个点是否在闭合区域内 (1,032)
  • WordPress分页插件 – WP-PageNavi的使用(替换现有脚本) (948)
  • OpenGL实现billboard效果(CPU) (865)
  • Midas W-满堂支架快速建模助手开发 (837)
  • 从DLL中动态加载一个函数:LoadLibrary和GetProcAddress的使用 (748)
  • Midas XD [错误] 右侧挡土墙的最下端深度必须小于地基的最下端深度 (709)
  • 两跨连续梁影响线绘制-附源码 (686)
  • 土木想往土木软件开发方向发展,应该如何准备 (680)
  • OpenGL几何着色器实现贝塞尔曲线 (664)
  • 通过Spy++抓取窗口以查询对话框id (613)
  • 使用ODA数据库出现 “ODA_ASSUME”: 找不到标识符的错误 (547)
  • #pragma message 编译时提示信息 (527)
  • OpenGL雾化效果实现-每像素雾化 (508)
  • midas XD2020的开发 (476)

分类

  • C# (3)
  • C++ (19)
  • GIS (1)
  • MFC (3)
  • ObjectARX (2)
  • OpenGL (11)
  • Revit开发 (1)
  • 学习笔记 (2)
  • 岩土 (2)
  • 算法 (1)
  • 结构设计 (7)
  • 职场生涯 (1)
  • 计算几何 (3)

归档

  • 2024 年 12 月 (1)
  • 2024 年 10 月 (1)
  • 2024 年 9 月 (1)
  • 2023 年 3 月 (2)
  • 2022 年 10 月 (1)
  • 2022 年 3 月 (1)
  • 2022 年 2 月 (1)
  • 2022 年 1 月 (5)
  • 2021 年 11 月 (7)
  • 2021 年 6 月 (3)
  • 2021 年 5 月 (2)
  • 2021 年 3 月 (2)
  • 2021 年 2 月 (8)
  • 2021 年 1 月 (18)

标签

3dtiles anr Bezier Curves BillBoard C++ CDN CivilDesigner DLL EasyX fog glTF MFC Midas W Midas XD NormalMapping ObjectARX ODA OpenGL OpenXML Open XML PBR revit WinDbg 基坑设计 影响线 截面特性 桥梁 桥梁检测 桥梁设计 算法 计算几何 设计模式

书签

  • 李燕良的CSDN
  • 崔济东的博客
  • C++爱好者博客
  • 陈学伟的博客
  • 贾苏的博客
  • 陈睦锋的博客
  • 孙勇的博客

统计

  • 0
  • 204
  • 103
  • 388
  • 145
  • 268,907
  • 77,790

实时访问地域

© 2025   liyanliang.net Copyright. All Rights Reserved.