Opengl绘制我们的小屋(三)纹理绘制

本准备先说光照相关实现,但是发现对那个模型实在看不下去了,于是先绘制纹理。

先看下基本纹理贴上去的显示效果。具体模型图请看上篇文章的实现,这篇只讲纹理实现。

我们常见的纹理绘制差不多如下,先写一个纹理坐标,然后是一个顶点坐标,GL.TexCoord2(1.0f,1.0f);GL.Vectex(1.f,1.f,1.f)。先说一下纹理坐标与顶点坐标的对应处理关系,为了好理解,我们只说二维纹理。先看下图。

我们设置一张16*8的纹理,如上图,我们设置GL.TexCoord2(1.0f,1.0f)就是在(16,8)位置,超出1的部分,会复制超出部分,如上图设置GL.TexCoord(2.0,2.0)就会在图上一共显示四张图,同理我们只要纹理的右下部分四分之一,那坐标应该分别是(1,0.5),(0.5,0.5),(0.5,0)(1,0).

在上个模型中,我们可以看到我们有很多的模型需要纹理贴图,如果采用一般的方法,需要在每个立方体的面上去计算我们相应的纹理坐标,这样花费时间太大,这样我们采用opengl里提供的自动生成纹理。首先要指定以什么样的模式(既什么样的算法)来生成纹理坐标。可以指定几种纹理坐标生成模式:GL_OBJECT_LINEAR, GL_EYE_LINEAR, GL_SPHERE_MAP等。

在这里我们只需要采用最容易理解的纹理生成模式。GL_OBJECT_LINEAR,在这种模式下,指定四个参数,p0,p1,p2,p3,对应顶点(x0,y0,z0,w0),生成的纹理坐标为p0*x0+p1*y0+p2*z0+p3*w0。

我们再来看我们的需求

1。要对立方体的各面自动生成纹理。

2。我们贴地面的,要像砖是一块一块的,而贴墙时,只需要贴一张。如最上图所绘,左下角与右上角分别对应的纹理坐标在贴砖是一块一块的应该是((0,0),(n,m))《m>0,n>0》,而贴墙时应该对应((0,0),(1,1))。

借用上篇的图。

第一点,根据我们的p0*x0+p1*y0+p2*z0+p3*w0算法来看,比如贴地面时,也就是垂直于Y轴时,用到的是图上的(4,5,1,0)这个面。

 想象一下对应关系,纹理的S轴坐标对应是1-0这条线,T轴坐标对应的是1-5这条线,那么对应s轴大致如下[x;0.;0.;0],T轴坐标大致如下[0;0.;y;0].各面按照这样得到对应的参数。相关代码如下。

 1         member this.GenTexture(vector:Vector3,ball) = 
 2             GL.Enable(EnableCap.TextureGenS)
 3             GL.Enable(EnableCap.TextureGenT)
 4             GL.TexGen(TextureCoordName.S,TextureGenParameter.TextureGenMode,int TextureGenMode.ObjectLinear)
 5             GL.TexGen(TextureCoordName.T,TextureGenParameter.TextureGenMode,int TextureGenMode.ObjectLinear)
 6             let mutable xa = [|1.f;0.f;0.f;0.f|]
 7             let mutable xy = [|0.f;1.f;0.f;0.f|]
 8             let mutable x,y,z=1.f,1.f,0.f
 9             if ball then z <-0.5f
10             if vector = Vector3.UnitY || vector = -Vector3.UnitY then 
11                 if ball then
12                     x<-1.0f/Vector3.Subtract(v8.[0],v8.[1]).Length
13                     y<-1.0f/Vector3.Subtract(v8.[0],v8.[4]).Length
14                 xa <- [|x;0.f;0.f;z|]
15                 xy <- [|0.f;0.f;y;z|]
16             if vector = Vector3.UnitZ || vector = -Vector3.UnitZ then 
17                 if ball then
18                     x<-1.0f/Vector3.Subtract(v8.[0],v8.[1]).Length
19                     y<-1.0f/Vector3.Subtract(v8.[0],v8.[3]).Length
20                 xa <- [|x;0.f;0.f;z|]
21                 xy <- [|0.f;y;0.f;z|]
22             if vector = Vector3.UnitX || vector = -Vector3.UnitX then 
23                 if ball then                
24                     x<-1.0f/Vector3.Subtract(v8.[0],v8.[4]).Length
25                     y<-1.0f/Vector3.Subtract(v8.[0],v8.[3]).Length
26                 xa <- [|0.f;0.f;x;z|]
27                 xy <- [|0.f;y;0.f;z|]
28             GL.TexGen(TextureCoordName.S,TextureGenParameter.ObjectPlane,xa)
29             GL.TexGen(TextureCoordName.T,TextureGenParameter.ObjectPlane,xy)
View Code

其中ball参数表示贴墙这种一面只帖一张纹理。在ball时,我们可以看到我们的p0,p1,p2,p3,p3=0.5,这是因为我的矩形的画法所导致的(前看上篇所叙),比如我要生成宽为8的立方体,其中我画顶点是x=-4,x=4这种画法,如果不加0.5,那么我的帖图就会是左下到右上是(-0,5,-0,5),(0.5,0.5)这种。

在opengl是状态机模式,记的每画一面打开相应的状态后,在画完后,需要关掉,不然会影响下一部分的贴图。绘制部分的代码如下。

 1         member this.DrawTexTure(ts:int*int*bool,vector:Vector3,indexs:int []) =
 2             let ind,txtId,ball = ts
 3             if txtId <> 0  then 
 4                 GL.BindTexture(TextureTarget.Texture2D,txtId)
 5                 this.GenTexture(vector,ball)
 6             GL.Normal3(vector)
 7             GL.DrawElements(BeginMode.Triangles,6,DrawElementsType.UnsignedInt,indexs)
 8             GL.Disable(EnableCap.TextureGenS)
 9             GL.Disable(EnableCap.TextureGenT)
10             GL.Disable(EnableCap.TextureGenR)
11             GL.Disable(EnableCap.TextureGenQ)
View Code

最后附上源码与可执行文件。操作方式和网游一样,鼠标右键按下加鼠标上下左右移动是视角.EDSF行走。

 纹理小室版

下一章节会给大家讲到光照的运用。

 

posted @ 2013-11-15 00:51  天天不在  阅读(6218)  评论(0编辑  收藏  举报