BUMPMAPPING WITH GLSL

BUMPMAPPING WITH GLSL

当我开始学习凹凸贴图和视差映射时,我发现很多教程涉及一个简单的矩形,但没有什么接近现实生活的用途:

这是我填补空白的难题。

 

概念

BumpMapping允许设计师通过100,000多个多边形生物表达自己的创造力。一旦艺术完成,就会自动生成低聚合模型(5000多边形)以及法线贴图。

在运行时,通过将低模型与法线贴图组合来加回细节。


照明模型。

细节被添加到低聚合物表面反应的光。照明方程是Blinn-Phong,其中:(

pixelColor= Ambient + (Diffuse + Specular) * Shadow但让我们忘记阴影)。

Ambient = ambientMaterial * ambientLight

Diffuse = diffuseMaterial * diffuseLight * lamberFactor
lamberFactor = max (dot (lightVec, normal), 0.0)

Specular = specularMaterial * specularLight * speculatCoef
speculatCoef = pow (max (dot (halfVec, normal), 0.0), shininess)

细节:

  • 环境几乎是一个常数。
  • 漫射取决于光矢量与表面法向量之间的角度。
  • 镜面取决于眼矢量与表面法向量之间的角度。

注意:当我们处理法向量时,可以用简单的点积来获得余弦值。

通常,每个计算都是在眼睛空间中进行的,但是在凹凸映射中,法线图中的法向量表示为切线空间。因此,我们需要转换所有需要的向量。为了做到这一点,我们使用矩阵:Eye space - > Tangent space。


切线空间数学。

每个顶点的矩阵如下:

				
				
	[Normal.x Normal.y Normal.z]
	[BiNormal.x BiNormal.y BiNormal.z]
	[Tangent.x tangent.y Tangent.z]
	

正常很容易计算。一个简单的交叉产品每张脸。顶点的法线等于法线(与该顶点相关的所有面)的和,最后归一化。

			
	对于模型中的每个面
	{
		通过交叉产品产生面部正常
		在每个顶点前面加上法线
	}
	
	对于模型中的每个顶点
		规范正态矢量
					
					


对于正切和二次正交,您可以在任何好的数学书中找到解决方案(我强烈推荐3D游戏编程数学)。这是一个代码示例:

			
	generateNormalAndTangent(float3 v1,float3 v2,text2 st1,text2 st2)
	{
		float3 normal = v1.crossProduct(v2);
		
		float coef = 1 /(st1.u * st2.v  -  st2.u * st1.v);
		float3切线;

		tangent.x = coef *((v1.x * st2.v)+(v2.x * -st1.v));
		tangent.y = coef *((v1.y * st2.v)+(v2.y * -st1.v));
		tangent.z = coef *((v1.z * st2.v)+(v2.z * -st1.v));
		
		float3 binormal = normal.crossProduct(tangent);
	}
				


就像法线一样:对于连接到该顶点的每个面积累积切线和次数,然后通过归一化进行平均。

在您的实现中,尝试可视化您生成的矢量,它们需要一致,因为它们将被GPU插值。

CPU侧

在openGL方面,必须做一些事情:

  • 绑定顶点数组
  • 绑定正常数组
  • 绑定纹理坐标数组
  • 绑定元素索引数组
  • 将切线数组绑定到着色器
  • 绑定颜色纹理
  • 绑定法线贴图(凹凸贴图)
  • 绑定高度贴图纹理(视差映射)
	
	//为了动画目的,每个帧更新顶点VBO
	glBindBufferARB(GL_ARRAY_BUFFER_ARB,vboVertexId);
	glVertexPointer(3,GL_FLOAT,0,0);

	//与顶点VBO相同:每帧更新
	glBindBufferARB(GL_ARRAY_BUFFER_ARB,vboNormalId);
	glNormalPointer(GL_FLOAT,0,0);

	// VBO,创建和填充一次,纹理坐标永远不会改变
	glBindBufferARB(GL_ARRAY_BUFFER_ARB,vboTexturId);
	glTexCoordPointer(2,GL_FLOAT,0,0);

	//切线生成以前,不需要通过二进制,一个十字制品就会生成它
	glVertexAttribPointerARB(tangentLoc,3,GL_FLOAT,GL_FALSE,0,tangentArraySkinPointer);

	// VBO,创建和填充一次,要绘制的元素永远不会改变 
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB,vboElementsId);
	glDrawElements(GL_TRIANGLES,meshes [i] .facesCount * 3,GL_UNSIGNED_INT,0);

	
	glActiveTextureARB(GL_TEXTURE0);
	glBindTexture(diffuseTextureId);
	glUniform1iARB(diffuseTextureUniform,0);
	
	glActiveTextureARB(GL_TEXTURE1);
	glBindTexture(normalTextureId);
	glUniform1iARB(normalTextureUniform,0);
	
	glActiveTextureARB(GL_TEXTURE2);
	glBindTexture(heightTextureId);
	glUniform1iARB(heightTextureUniform,0);
			

GPU边

顶点着色器的作用是构建Blinn-Phong模型中使用的矩阵和旋转矢量,因此:

  • 用正交和正切的交叉积产生双切线。
  • 组合三个向量以形成旋转矩阵,从相机空间到切线空间。
  • 旋转光源和相机矢量。

在片段着色器中:

  • 从法线贴图中检索正常坐标。
  • 将值从[-1,1]转换为[0,1]。
  • 计算角度,产生漫反射,漫反射和镜面反射。
  • 添加漫反射,漫反射和镜面成分。

顶点着色器

	
	属性vec3切线
	变化的vec3 lightVec;
	变化vec3 halfVec;
	改变vec3 eyeVec;
	

  void main()
  {

	gl_TexCoord [0] = gl_MultiTexCoord0;
	
	//构建矩阵眼空间 - >切线空间
	vec3 n = normalize(gl_NormalMatrix * gl_Normal);
	vec3 t =归一化(gl_NormalMatrix * tangent);
	vec3 b =交叉(n,t);
	
	vec3顶点位置= vec3(gl_ModelViewMatrix * gl_Vertex);
	vec3 lightDir = normalize(gl_LightSource [0] .position.xyz  -  vertexPosition);
		
		
	//通过切线转换光和半角矢量
	vec3 v
	vx = dot(lightDir,t);
	vy = dot(lightDir,b);
	vz = dot(lightDir,n);
	lightVec = normalize(v);
	
	  
	vx = dot(vertexPosition,t);
	vy = dot(vertexPosition,b);
	vz = dot(vertexPosition,n);
	eyeVec = normalize(v);
	
	
	vertexPosition = normalize(vertexPosition);
	
	/ *归一化halfVector以将其传递给片段着色器* /

	//不需要除以2,结果就是归一化。
	// vec3 halfVector = normalize((vertexPosition + lightDir)/ 2.0); 
	vec3 halfVector = normalize(vertexPosition + lightDir);
	vx = dot(halfVector,t);
	vy = dot(halfVector,b);
	vz = dot(halfVector,n);

	//不需要归一化,t,b,n和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 * texture2D(normalTexture,gl_TexCoord [0] .st).rgb  -  1.0;
	normal = normalize(normal);
	
	//计算漫射照明
	float lamberFactor = max(dot(lightVec,normal),0.0);
	vec4 diffuseMaterial = 0.0;
	vec4 diffuseLight = 0.0;
	
	//计算镜面照明
	vec4镜面材料;
	vec4镜面光
	浮萍
  
	//计算环境
	vec4 ambientLight = gl_LightSource [0] .ambient;	
	
	if(lamberFactor> 0.0)
	{
		diffuseMaterial = texture2D(diffuseTexture,gl_TexCoord [0] .st);
		diffuseLight = gl_LightSource [0] .diffuse;
		
		//在doom3中,镜面值来自纹理 
		镜面材料= vec4(1.0);
		specularLight = gl_LightSource [0] .specular;
		shininess = pow(max(dot(halfVec,normal),0.0),2.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引擎的配置示例。


下载

2010年4月5日:似乎二进制分发在Windows 7中不起作用。一旦我有一段时间,我必须看看这个。

推荐阅读

几本书,了解更多关于凹凸贴图和视差映射的书籍。Doom3是一个很好的资源来学习,每个模型都可以轻松访问和纯文本。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值