使用前面學過的技術(shù)已經(jīng)可以利用OpenGL ES構(gòu)建立體圖形,并通過頂點著色器和片元著色器對其進行各種變化呢和光照等效果使得三維效果更加真實,實際上我看看到很多的3D游戲漂亮多了,那是因為有各種各樣的漂亮的圖像帶給人很多視覺盛宴,這篇文章在前面的基礎(chǔ)上,增加物體的表面貼圖,使得物體更加好看。
紋理概念
紋理用來表示圖像照片或者說一系列的數(shù)據(jù),使用紋理可以使物體用用更多的細節(jié)。OpenGL ES 2.0 中有兩種貼圖:二維紋理和立方體紋理。
每個二維紋理都由許多小的紋理元素組成,類似與片元和像素,使用紋理最簡單的方式就是直接從一個圖像加載數(shù)據(jù)。在OpenGL中規(guī)定紋理圖像的左下角由stst坐標(0.0,0.0)指定,右上角由stst坐標(1.0,1.0)指定,不過超過1.0的坐標也是允許的,在該區(qū)間之外的紋理在讀取時的時候由紋理拉伸模式?jīng)Q定。
OpenGL ES 2.0不必是正方形,但是每個維度都應(yīng)該是2的冪
在Android中使用的OpenGL ES的紋理坐標系跟官方的紋理坐標系統(tǒng)不一樣,在Android中使用官方的紋理坐標系統(tǒng),得到的結(jié)果是相反的,而是左上角是stst坐標(0.0,0.0)點,右下角是stst坐標(1.0,1.0)點。
二維紋理映射的原理
使用紋理就是在紋理圖中進行采樣,因此需要將選定的紋理坐標穿進頂點著色器,經(jīng)過插值在片元著色器中從紋理圖中的指定位置采樣即可,紋理圖的數(shù)據(jù)通過往片元插值器傳遞紋理單元指定的。
紋理對象和紋理加載
創(chuàng)建一個紋理對象,保存渲染所需的紋理數(shù)據(jù),例如圖像數(shù)據(jù)、過濾模式、包裝模式。創(chuàng)建生成紋理對象的函數(shù)
1
2
3
4
5
|
public static native void glGenTextures( int n, // 指定要生成的紋理對象的數(shù)量 int [] textures, // 保存紋理對象ID的數(shù)組 int offset ); |
紋理對象在應(yīng)用程序中不再使用時,需要刪除。
1
2
3
4
5
|
public static native void glDeleteTextures( int n, // 指定要刪除的紋理數(shù)量 int [] textures, // 保存待刪除的紋理ID的數(shù)組 int offset ); |
紋理對象的 ID 必須是 glGenTextures 產(chǎn)生的,一旦生成紋理ID,就必須綁定紋理對象才能繼續(xù)進行后續(xù)的操作。后續(xù)的操作將影響綁定的紋理對象。一旦紋理被綁定到一個特定的紋理目標,再刪除之前就一直保持著綁定狀態(tài)。
1
2
3
4
|
public static native void glBindTexture( int target, // 綁定紋理對象到目標 GL_TEXTURE_2D 或 GL_TEXTURE_CUBE_MAP int texture // 要綁定的紋理對象ID ); |
激活某個紋理單元
1
2
3
|
public static native void glActiveTexture( int texture // 要激活的紋理單元 ); |
對這兩個函數(shù)的理解:顯卡中有N個紋理單元(GL_TEXTURE0,GL_TEXTURE1,GL_TEXTURE2…),每個紋理單元中保存著很多紋理目標(targetTexture1D,targetTexture2D,targetTexture3D,targetTextureCube…),OpenGL ES 2.0貌似只支持了targetTexture2D和targetTextureCube。
紋理單元TextureUnit的定義如下
1
2
3
4
5
6
7
8
|
struct TextureUnit { GLuint targetTexture1D; GLuint targetTexture2D; GLuint targetTexture3D; GLuint targetTextureCube; ... }; |
glActiveTexture函數(shù)就是設(shè)置當前活動的紋理單元
1
2
3
4
5
6
7
|
TextureUnit textureUnits[GL_MAX_TEXTURE_IMAGE_UNITS] GLuint currentTextureUnit = 0 ; // ... void glActiveTexture(GLenum textureUnit) { currentTextureUnit = textureUnit - GL_TEXTURE0 ; } |
glBindTexture函數(shù)就是將紋理對象ID賦值給當前活動的紋理單元的對應(yīng)的目標紋理。
1
2
3
4
5
6
7
8
9
10
11
|
void glBindTexture(GLenum textureTarget, GLuint textureObject) { TextureUnit *texUnit = &textureUnits[currentTextureUnit]; switch (textureTarget) { case GL_TEXTURE_1D: texUnit->targetTexture1D = textureObject; break ; case GL_TEXTURE_2D: texUnit->targetTexture2D = textureObject; break ; case GL_TEXTURE_3D: texUnit->targetTexture3D = textureObject; break ; case GL_TEXTURE_CUBEMAP: texUnit->targetTextureCube = textureObject; break ; } } |
獲取一副圖片的紋理數(shù)據(jù)
1
2
3
4
5
|
public static void texImage2D( int target, // 常數(shù)GL_TEXTURE_2D int level, // 表示多級分辨率的紋理圖像的級數(shù),若只有一種分辨率,則level設(shè)為0。 Bitmap bitmap, int border // 邊框,一般設(shè)為0 ) |
其他紋理選項的設(shè)置使用glTexParameterf系列函數(shù)
1
2
3
4
5
|
public static native void glTexParameterf( int target, int pname, // 設(shè)定的參數(shù),可以是GL_TEXTURE_MAG_FILTER,GL_TEXTURE_MIN_FILTER,GL_TEXTURE_WRAP_S,GL_TEXTURE_WRAP_T float param // 參數(shù)對應(yīng)的值 ); |
應(yīng)用紋理的例子
對前面的立方體的每個面應(yīng)用一張圖片作為紋理貼圖,效果圖(這個紋理圖是哪個老師來著?)
Rectangle.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
public class Rectangle { private FloatBuffer mVertexBuffer; private int mProgram; private int mPositionHandle; private int muMVPMatrixHandle; private int mColorHandle; private int muMMatrixHandle; private int muLightLocationHandle; private int mTextureCoordHandle; private int textureId; private int muTextureHandle; private Context mContext; public Rectangle(Context context) { this .mContext = context; initVetexData(); } public void initVetexData() { float vertices[] = new float [] { // 頂點 顏色 紋理坐標 //前面 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, //后面 0 , 0 ,- 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 0 , 0 ,- 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 0 , 0 ,- 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 0 , 0 ,- 1 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, //左面 - 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, - 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, - 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, //右面 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, //上面 0 , 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 0 , 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 0 , 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 0 , 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 , 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, //下面 0 ,- 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, 0 ,- 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 0 .0f, 0 .0f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 0 ,- 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, - 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 0 .0f, 1 .0f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 0 ,- 1 , 0 , 1 , 1 , 1 , 0 , 0 .5f, 0 .5f, 1 ,- 1 ,- 1 , 1 , 0 , 0 , 0 , 1 .0f, 1 .0f, 1 ,- 1 , 1 , 1 , 0 , 0 , 0 , 1 .0f, 0 .0f }; ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4 ); vbb.order(ByteOrder.nativeOrder()); mVertexBuffer = vbb.asFloatBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position( 0 ); int vertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); mProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(mProgram, vertexShader); GLES20.glAttachShader(mProgram, fragmentShader); GLES20.glLinkProgram(mProgram); mPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition" ); mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor" ); mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord" ); muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix" ); muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix" ); muLightLocationHandle = GLES20.glGetUniformLocation(mProgram, "uLightLocation" ); muTextureHandle = GLES20.glGetUniformLocation(mProgram, "uTexture" ); initTexture(); } // 初始化紋理 public void initTexture() { int [] textures = new int [ 1 ]; GLES20.glGenTextures( 1 , textures, 0 ); textureId = textures[ 0 ]; // 激活紋理單元,默認激活的就是0號紋理單元 //GLES20.glActiveTexture(GLES20.GL_TEXTURE0); // 將紋理對象ID綁定到當前活動的紋理單元0上的GL_TEXTURE_2D目標 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); // 后面對紋理的設(shè)置都是對綁定了的紋理所生效的 //縮小采樣使用最近點采樣 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST); //縮小采樣使用最近點采樣 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR); //紋理包裹拉伸方式在st軸采用截取拉伸方式,這些設(shè)置指的是對坐標范圍超過1的部分的限制 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE); Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.texture); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0 , bitmap, 0 ); // 圖片已經(jīng)加載到了顯存,可以回收 bitmap.recycle(); } public void draw() { GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0 , 12 * 6 ); } public void setValue( float [] mvpMatrix, float [] mMatrix) { GLES20.glUseProgram(mProgram); mVertexBuffer.position( 0 ); GLES20.glVertexAttribPointer(mPositionHandle, 3 , GLES20.GL_FLOAT, false , ( 4 + 3 + 2 ) * 4 , mVertexBuffer); mVertexBuffer.position( 3 ); GLES20.glVertexAttribPointer(mColorHandle, 4 , GLES20.GL_FLOAT, false , ( 4 + 3 + 2 ) * 4 , mVertexBuffer); mVertexBuffer.position( 7 ); GLES20.glVertexAttribPointer(mTextureCoordHandle, 2 , GLES20.GL_FLOAT, false , ( 4 + 3 + 2 ) * 4 , mVertexBuffer); GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glEnableVertexAttribArray(mColorHandle); GLES20.glEnableVertexAttribArray(mTextureCoordHandle); GLES20.glUniform3f(muLightLocationHandle, 0 , 0 , 20 ); GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1 , false , mvpMatrix, 0 ); GLES20.glUniformMatrix4fv(muMMatrixHandle, 1 , false , mMatrix, 0 ); // 將使用的紋理單元0傳遞給片元著色器 GLES20.glUniform1i(muTextureHandle, 0 ); } private int loaderShader( int type, String shaderCode) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; } private String vertexShaderCode = "uniform mat4 uMVPMatrix;" + "attribute vec2 aTextureCoord;" + "varying vec2 vTextureCoord;" + "uniform mat4 uMMatrix;" + "uniform vec3 uLightLocation;" + "attribute vec4 aColor;" + "varying vec4 vColor;" + "varying vec4 vDiffuse;" + "attribute vec3 aPosition;" + "void main(){" + "vec3 normalVectorOrigin = aPosition;" + "vec3 normalVector = normalize((uMMatrix*vec4(normalVectorOrigin,1)).xyz);" + "vec3 vectorLight = normalize(uLightLocation - (uMMatrix * vec4(aPosition,1)).xyz);" + "float factor = max(0.0, dot(normalVector, vectorLight));" + "vDiffuse = factor*vec4(1,1,1,1.0);" + "gl_Position = uMVPMatrix * vec4(aPosition,1);" + "vColor = aColor;" + "vTextureCoord = aTextureCoord;" // 將紋理坐標傳到片元著色器,得到更多的插值紋理坐標 + "}" ; private String fragmentShaderCode = "precision mediump float;" + "uniform sampler2D uTexture;" // 這個uniform變量表示了紋理數(shù)據(jù),從java中傳過來的是所在的紋理單元編號 + "varying vec2 vTextureCoord;" + "varying vec4 vColor;" + "varying vec4 vDiffuse;" + "void main(){" + "gl_FragColor = (vColor*vDiffuse + vColor*vec4(0.6,0.6,0.6,1))*texture2D(uTexture, vTextureCoord);" // 在紋理的基礎(chǔ)上還考慮到光照,texture2D函數(shù)用于紋理采樣 + "}" ; } |
需要注意的還是傳入的頂點的時候數(shù)組里面包含了頂點、顏色和紋理坐標,因此要用ByteBuffer的position方法定位。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/cauchyweierstrass/article/details/52947279