BUMPMAPPING WITH GLSL BUMPMAPPING WITH GLSL原文链接 https://fabiensanglard.net/bumpMapping/index.php当 我 开始 学习 凹凸贴图 和 视差映射 时我发现 很多 教程 涉及 一个 简单的矩形但没有什么 接近 现实生活 的 用途这是我填补空白的难题。概念BumpMapping允许 设计师 通过10 0000 也就是 10万个原来 表述为 100000多个 多边形 生物 表达 自己 的 创造力。一旦 艺术 完成就会自动 生成 低聚合模型5000多边形以及 法线贴图。在 运行 时通过将 低模型 与 法线贴图 组合 来 加回 细节。照明模型。细节 被添加到 低聚合物 此处 翻译 可能 有误 表面反应 的 光。照明方程 是 Blinn-Phong其中:(pixelColor Ambient (Diffuse Specular) * Shadow但让我们忘记阴影。// 此处可能有误……Ambient ambientMaterial * ambientLightDiffuse diffuseMaterial * diffuseLight * lamberFactorlamberFactor max (dot (lightVec, normal), 0.0)Specular specularMaterial * specularLight * speculatCoefspeculatCoef pow (max (dot (halfVec, normal), 0.0), shininess)细节环境 几乎是一个 常数。漫射 取决于 光矢量 与表面法向量之间的角度。镜面取决于 视觉 我自己认为应该是 视觉向量矢量 与表面法向量之间的角度。注意当我们处理法向量时可以用 简单的 点积 来获得 余弦值。通常每个计算 都是在 eye 空间 中 进行 的但是在凹凸映射中法线图中的法向量 表示为 切线空间。因此我们需要转换 所有需要的 向量。为了做到这一点我们使用矩阵 Eye space - Tangent space。切线 空间 数学。每个 顶点的 矩阵 如下[Normal.x Normal.y Normal.z] [BiNormal.x BiNormal.y BiNormal.z] [Tangent.x tangent.y Tangent.z]法线 很容易 计算。一个简单的 交叉产品 每张脸。应该是每个面顶点的 法 线 等于 法线与该顶点相关的所有面的 和最后 归一化。对于模型中的每个面 { 通过交叉产品产生面部正常 在每个顶点前面加上法线 } 对于模型中的每个顶点 规范正态矢量对于 正切 和 二次 正交您可以在 任何 好的数学书 中找到 解决方案我强烈 推荐3D游戏编程数学。这是一个代码示例generateNormalAndTangentfloat3 v1float3 v2text2 st1text2 st2 { float3 normal v1.crossProductv2; float coef 1 /st1.u * st2.v - st2.u * st1.v; float3切线; tangent.x coef *v1.x * st2.vv2.x * -st1.v; tangent.y coef *v1.y * st2.vv2.y * -st1.v; tangent.z coef *v1.z * st2.vv2.z * -st1.v; float3 binormal normal.crossProducttangent; }就像法线一样对于 连接 到 该顶点的每个 面积 累积 切线 和 次数然后 通过 归一化 进行 平均。在您的实现中尝试可视化 您生成的矢量它们需要一致因为它们将被 GPU插值。CPU侧在openGL方面必须做一些事情绑定 顶点数组绑定正常数组绑定 纹理 坐标 数组绑定 元素 索引 数组将 切线数组 绑定到 着色器绑定 颜色纹理绑定 法线贴图凹凸贴图绑定 高度贴图纹理 视差映射//为了动画目的每个帧更新顶点VBO glBindBufferARBGL_ARRAY_BUFFER_ARBvboVertexId; glVertexPointer3GL_FLOAT00; //与顶点VBO相同每帧更新 glBindBufferARBGL_ARRAY_BUFFER_ARBvboNormalId; glNormalPointerGL_FLOAT00; // VBO创建和填充一次纹理坐标永远不会改变 glBindBufferARBGL_ARRAY_BUFFER_ARBvboTexturId; glTexCoordPointer2GL_FLOAT0,0; //切线生成以前不需要通过二进制一个十字制品就会生成它 glVertexAttribPointerARBtangentLoc3GL_FLOATGL_FALSE0tangentArraySkinPointer; // VBO创建和填充一次要绘制的元素永远不会改变 glBindBufferARBGL_ELEMENT_ARRAY_BUFFER_ARBvboElementsId; glDrawElementsGL_TRIANGLESmeshes [i] .facesCount * 3GL_UNSIGNED_INT0; glActiveTextureARBGL_TEXTURE0; glBindTexturediffuseTextureId; glUniform1iARBdiffuseTextureUniform0; glActiveTextureARBGL_TEXTURE1; glBindTexturenormalTextureId; glUniform1iARBnormalTextureUniform0; glActiveTextureARBGL_TEXTURE2; glBindTextureheightTextureId; glUniform1iARBheightTextureUniform0;GPU边顶点着色器的作用是构建Blinn-Phong模型 中 使用的 矩阵 和 旋转矢量因此用 正交 和 正切的 交叉积 产生 双切线。组合 三个 向量以 形成 旋转矩阵从 相机空间 到 切线空间。旋转 光源 和 相机矢量。在片段着色器中从 法线贴图 中 检索 正常坐标。将 值从[-1,1]转换为[0,1]。计算 角度产生漫反射漫反射 和 镜面反射。添加 漫反射漫反射 和 镜面成分。顶点着色器属性vec3切线 变化的vec3 lightVec; 变化vec3 halfVec; 改变vec3 eyeVec; void main { gl_TexCoord [0] gl_MultiTexCoord0; //构建矩阵眼空间 - 切线空间 vec3 n normalizegl_NormalMatrix * gl_Normal; vec3 t 归一化gl_NormalMatrix * tangent; vec3 b 交叉nt; vec3顶点位置 vec3gl_ModelViewMatrix * gl_Vertex; vec3 lightDir normalizegl_LightSource [0] .position.xyz - vertexPosition; //通过切线转换光和半角矢量 vec3 v vx dotlightDirt; vy dotlightDirb; vz dotlightDirn; lightVec normalizev; vx dotvertexPositiont; vy dotvertexPositionb; vz dotvertexPositionn; eyeVec normalizev; vertexPosition normalizevertexPosition; / *归一化halfVector以将其传递给片段着色器* / //不需要除以2结果就是归一化。 // vec3 halfVector normalizevertexPosition lightDir/ 2.0; vec3 halfVector normalizevertexPosition lightDir; vx dothalfVectort; vy dothalfVectorb; vz dothalfVectorn; //不需要归一化tbn和halfVector是法向量。 //规范化v; halfVec v; gl_Position ftransform; }片段着色器uniform sampler2D diffuseTexture; 均匀采样器 //新的bumpmapping 变化的vec3 lightVec; 变化vec3 halfVec; 改变vec3 eyeVec; void main { //从法线图查找正常从[0,1]移动到[-1,1]范围进行归一化 vec3 normal 2.0 * texture2DnormalTexturegl_TexCoord [0] .st.rgb - 1.0; normal normalizenormal; //计算漫射照明 float lamberFactor maxdotlightVecnormal0.0; vec4 diffuseMaterial 0.0; vec4 diffuseLight 0.0; //计算镜面照明 vec4镜面材料; vec4镜面光 浮萍 //计算环境 vec4 ambientLight gl_LightSource [0] .ambient; iflamberFactor 0.0 { diffuseMaterial texture2DdiffuseTexturegl_TexCoord [0] .st; diffuseLight gl_LightSource [0] .diffuse; //在doom3中镜面值来自纹理 镜面材料 vec41.0; specularLight gl_LightSource [0] .specular; shininess powmaxdothalfVecnormal0.02.0; gl_FragColor diffuseMaterial * diffuseLight * lamberFactor; gl_FragColor specularMaterial * specularLight * shininess; } gl_FragColor ambientLight; }结果注意影子组件 不在 着色器 片段 中但可以在 下载的代码中 找到 它。视频该视频显示一个2000多边形Hellknight原始模型。型号与512x512凹凸贴图。模型具有512x512凹凸贴图和漫反射/镜面映射。模型与512x512凹凸贴图和漫反射/镜面映射和阴影。该代码具有C md5模型查看器您可以通过config.cfg配置很多并在scene.cfg中定义场景。我包括地狱骑士md5所以任何人都可以运行演示我希望这将被容忍作为教育的目的。ZenFrag引擎的配置示例。下载来源。源码清理了linux。。Windows构建。Mac OSX构建。2010年4月5日似乎二进制分发在Windows 7中不起作用。一旦我有一段时间我必须看看这个。推荐阅读几本书了解更多关于凹凸贴图和视差映射的书籍。Doom3是一个很好的资源来学习每个模型都可以轻松访问和纯文本。

相关新闻

最新新闻

2026年Java面试核心要点与云原生技术解析

2026年Java面试核心要点与云原生技术解析

1. 2026版Java面试核心要点全景解析作为经历过三次技术周期更迭的Java老兵,我亲眼目睹了Java面试从基础概念考察到系统设计深挖的演变过程。2026年的Java技术栈已经形成了更为清晰的层次结构:语言基础作为地基、框架生态作为支柱、云原生与AI工程化构成穹…

2026/8/23 2:20:41
技术面试中如何回答「你的缺点是什么」

技术面试中如何回答「你的缺点是什么」

1. 面试高频题「你的缺点是什么」的底层逻辑 技术岗面试中,「你的缺点是什么」这个问题看似简单,实则暗藏玄机。面试官通过这个问题主要考察三个维度: 自我认知能力 :你是否能客观评估自己的技术短板 改进意识 :发…

2026/8/23 2:20:41
深入解析方法重写:从动态绑定到多态实现的核心机制

深入解析方法重写:从动态绑定到多态实现的核心机制

1. 当父类与子类方法“撞名”:一个看似简单却暗藏玄机的起点在面向对象编程的世界里,继承是构建复杂、可复用代码体系的基石。它允许我们基于已有的类(父类)创建新的类(子类),子类自动获得父类的…

2026/8/23 2:20:41
高校就业招聘系统:SpringBoot+Vue+MySQL毕业设计实践

高校就业招聘系统:SpringBoot+Vue+MySQL毕业设计实践

1. 项目概述:高校就业招聘系统设计与实现高校就业招聘系统是连接学生、企业和学校三方的数字化平台,旨在解决传统校园招聘中信息不对称、流程繁琐、效率低下等问题。这个基于SpringBootVueMySQL的毕业设计项目,不仅符合计算机专业毕业设计的核…

2026/8/23 2:20:41
TRACES基准:AI从知识复现到主动发现的评估革命

TRACES基准:AI从知识复现到主动发现的评估革命

大家好,我是专注于前沿技术分享的博主。最近,AI领域又迎来了一项重要的基准测试——TRACES。对于从事AI研究、大模型评估或智能体开发的开发者而言,理解这个新基准至关重要。它不再仅仅测试模型“知道”什么,而是开始挑战模型“发…

2026/8/23 2:20:41
Bandizip:现代解压缩工具如何通过细节设计提升工作效率

Bandizip:现代解压缩工具如何通过细节设计提升工作效率

上周帮同事处理一个压缩包,解压时弹窗广告、捆绑安装、密码错误、文件损坏……一连串问题折腾了半小时。那一刻我突然意识到,很多人还在用着十几年前的老工具,忍受着各种弹窗、广告和兼容性问题,却不知道解压缩软件这个看似简单的…

2026/8/23 2:15:41