OpenGL ES 纹理无法正确渲染
OpenGL ES Texture not rendering correctly
我正在使用 openglES 3.0 开发一个 Android 应用程序,我想创建一个 obj 加载器系统并显示一个带有纹理的 3D 模型。
我的代码正确显示了没有纹理的 3D 网格。
如果我尝试添加纹理,它会显示纹理并且纹理的某些部分将是空三角形。
示例:
我的带有纹理的网格
我找不到问题所在。
我的片段着色器
precision mediump float;
uniform vec4 vColor;
uniform sampler2D uTexture;
varying vec2 oTexCoordinate;
void main() {
gl_FragColor = texture2D(uTexture, oTexCoordinate);
//gl_FragColor = vec4(1, 0.5, 0, 1.0);
}
我的顶点着色器
attribute vec4 position;
uniform mat4 matrix;
attribute vec2 vTexCoordinate;
varying vec2 oTexCoordinate;
void main() {
oTexCoordinate = vTexCoordinate;
gl_Position = matrix * position;
}
提前致谢。
更新:
谢谢。
我已经更改了我的代码以适应你的想法,我已经激活了面部剔除并为法线添加了一个缓冲区,我暂时不用它。我把我的新代码放在下面。
但是问题并没有完全解决,见下图
我想我需要将法线信息传递给着色器,但我不确定如何正确执行或是否是解决方案。
public class MeshLoader {
private int program;
private List<String> facesVertexList;
private List<String> facesTextureList;
private List<String> facesNormalList;
private List<String> verticesList;
private List<String> textureList;
private List<String> normalList;
private FloatBuffer verticesBuffer;
private FloatBuffer verticesBufferTemp;
private FloatBuffer facesVertexBuffer;
private FloatBuffer facesTextureBuffer;
private FloatBuffer facesNormalBuffer;
private FloatBuffer textureBuffer;
private FloatBuffer textureBufferTemp;
private FloatBuffer normalBuffer;
private FloatBuffer normalBufferTemp;
private Context contextMeshLoader;
final int[] textureHandle = new int[1];
public MeshLoader(Context context) {
contextMeshLoader = context;
textureList = new LinkedList<>();
verticesList = new LinkedList<>();
normalList = new LinkedList<>();
facesVertexList = new LinkedList<>();
facesTextureList = new LinkedList<>();
facesNormalList = new LinkedList<>();
openObjFile(0);
String vertexShaderCode = "";
try{
InputStream vertexShaderStream = context.getResources().openRawResource(R.raw.vertex_shader);
vertexShaderCode = IOUtils.toString(vertexShaderStream, Charset.defaultCharset());
vertexShaderStream.close();
}
catch (Exception e){
Log.e("MeshReaderActivity", "Error reading vertex shader", e);
}
String fragmentShaderCode = "";
try{
InputStream fragmentShaderStream = context.getResources().openRawResource(R.raw.fragment_shader);
fragmentShaderCode = IOUtils.toString(fragmentShaderStream, Charset.defaultCharset());
fragmentShaderStream.close();
}
catch(Exception e){
Log.e("MeshReaderActivity", "Error reading fragment shader", e);
}
int vertexShader = GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER);
GLES30.glShaderSource(vertexShader, vertexShaderCode);
int fragmentShader = GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER);
GLES30.glShaderSource(fragmentShader, fragmentShaderCode);
GLES30.glCompileShader(vertexShader);
GLES30.glCompileShader(fragmentShader);
program = GLES30.glCreateProgram();
GLES30.glAttachShader(program, vertexShader);
GLES30.glAttachShader(program, fragmentShader);
GLES30.glLinkProgram(program);
GLES30.glUseProgram(program);
}
public void openObjFile(int value)
{
InputStream is;
value = 0;
if(value == 0)
is = contextMeshLoader.getResources().openRawResource(R.raw.objface);
else
is = contextMeshLoader.getResources().openRawResource(R.raw.objship);
if(verticesBufferTemp != null)
verticesBufferTemp.clear();
if(facesVertexBuffer != null)
facesVertexBuffer.clear();
if(textureBuffer != null)
textureBuffer.clear();
if(verticesList != null)
verticesList.clear();
if(facesVertexList != null)
facesVertexList.clear();
if(textureList != null)
textureList.clear();
try{
byte[] buffer = new byte[is.available()];
is.read(buffer);
String data = new String(buffer);
parseData(data);
ByteBuffer buffer2 = ByteBuffer.allocateDirect(facesVertexList.size() * 3 * 4);
buffer2.order(ByteOrder.nativeOrder());
facesVertexBuffer = buffer2.asFloatBuffer();
ByteBuffer buffer3 = ByteBuffer.allocateDirect(facesTextureList.size() * 3 * 4);
buffer3.order(ByteOrder.nativeOrder());
facesTextureBuffer = buffer3.asFloatBuffer();
ByteBuffer buffer6 = ByteBuffer.allocateDirect(facesTextureList.size() * 3 * 4);
buffer6.order(ByteOrder.nativeOrder());
facesNormalBuffer = buffer6.asFloatBuffer();
for(String face: facesVertexList) {
String vertexIndices[] = face.split("\s+");
float vertex1 = Float.parseFloat(vertexIndices[1]);
float vertex2 = Float.parseFloat(vertexIndices[2]);
float vertex3 = Float.parseFloat(vertexIndices[3]);
facesVertexBuffer.put((vertex1 - 1));
facesVertexBuffer.put((vertex2 - 1));
facesVertexBuffer.put((vertex3 - 1));
}
facesVertexBuffer.position(0);
for(String texture: facesTextureList){
String textureIndice[] = texture.split("\s+");
float texture1 = Float.parseFloat(textureIndice[1]);
float texture2 = Float.parseFloat(textureIndice[2]);
float texture3 = Float.parseFloat(textureIndice[3]);
facesTextureBuffer.put((texture1 - 1));
facesTextureBuffer.put((texture2 - 1));
facesTextureBuffer.put((texture3 - 1));
}
facesTextureBuffer.position(0);
for(String normal: facesNormalList) {
String normalIndice[] = normal.split("\s+");
float normal1 = Float.parseFloat(normalIndice[1]);
float normal2 = Float.parseFloat(normalIndice[2]);
float normal3 = Float.parseFloat(normalIndice[3]);
facesNormalBuffer.put((normal1 - 1));
facesNormalBuffer.put((normal2 - 1));
facesNormalBuffer.put((normal3 - 1));
}
facesNormalBuffer.position(0);
ByteBuffer buffer1 = ByteBuffer.allocateDirect(verticesList.size() * 3 * 4);
buffer1.order(ByteOrder.nativeOrder());
verticesBufferTemp = buffer1.asFloatBuffer();
ByteBuffer buffer5 = ByteBuffer.allocateDirect(textureList.size() * 2 * 4);
buffer5.order(ByteOrder.nativeOrder());
textureBufferTemp = buffer5.asFloatBuffer();
ByteBuffer buffer7 = ByteBuffer.allocateDirect(textureList.size() * 3 * 4);
buffer7.order(ByteOrder.nativeOrder());
normalBufferTemp = buffer7.asFloatBuffer();
for(String vertex: verticesList) {
String coords[] = vertex.split("\s+");
float x = Float.parseFloat(coords[1]);
float y = Float.parseFloat(coords[2]);
float z = Float.parseFloat(coords[3]);
verticesBufferTemp.put(x);
verticesBufferTemp.put(y);
verticesBufferTemp.put(z);
}
verticesBufferTemp.position(0);
for (String texture:textureList)
{
String textureIndices[] = texture.split("\s+");
float texture1 = Float.parseFloat(textureIndices[1]);
float texture2 = Float.parseFloat(textureIndices[2]);
textureBufferTemp.put(texture1);
textureBufferTemp.put(texture2);
}
textureBufferTemp.position(0);
for (String normal:normalList)
{
String normalIndices[] = normal.split("\s+");
float normal1 = Float.parseFloat(normalIndices[1]);
float normal2 = Float.parseFloat(normalIndices[2]);
normalBufferTemp.put(normal1);
normalBufferTemp.put(normal2);
}
normalBufferTemp.position(0);
System.out.println("size remaining " + facesVertexBuffer.remaining());
ByteBuffer bufferV = ByteBuffer.allocateDirect(facesVertexBuffer.remaining() * 3 * 4);
bufferV.order(ByteOrder.nativeOrder());
verticesBuffer = bufferV.asFloatBuffer();
ByteBuffer bufferT = ByteBuffer.allocateDirect(facesVertexBuffer.remaining() * 2 * 4);
bufferT.order(ByteOrder.nativeOrder());
textureBuffer = bufferT.asFloatBuffer();
ByteBuffer bufferN = ByteBuffer.allocateDirect(facesVertexBuffer.remaining() * 3 * 4);
bufferN.order(ByteOrder.nativeOrder());
normalBuffer = bufferN.asFloatBuffer();
int size = facesVertexBuffer.remaining();
for(int i = 0; i < size;i++)
{
int faceVertex = Math.round(facesVertexBuffer.get(i)) ;
int faceTexture = Math.round(facesTextureBuffer.get(i));
int faceNormal = Math.round(facesNormalBuffer.get(i));
float x = verticesBufferTemp.get((faceVertex)*3);
float y = verticesBufferTemp.get(((faceVertex)*3)+1);
float z = verticesBufferTemp.get(((faceVertex)*3)+2);
verticesBuffer.put( i*3, x);
verticesBuffer.put( (i*3)+1, y);
verticesBuffer.put( (i*3)+2, z);
float u = textureBufferTemp.get((faceTexture)*2);
float v = -textureBufferTemp.get(((faceTexture)*2)+1);
textureBuffer.put( i*2, u);
textureBuffer.put( (i*2)+1, v);
float xn = normalBufferTemp.get((faceNormal*3));
float yn = normalBufferTemp.get((faceNormal*3)+1);
float zn = normalBufferTemp.get((faceNormal*3)+2);
normalBuffer.put(i*3,xn);
normalBuffer.put((i*3)+1,yn);
normalBuffer.put((i*3)+2,zn);
}
verticesBuffer.position(0);
textureBuffer.position(0);
normalBuffer.position(0);
is.close();
loadTexture();
}
catch (Exception e) {
Log.e("MeshReaderActivity", "Error reading objfile", e);
}
}
public void parseData(String dataToParse)
{
Log.i("parse data method", "parse data method");
String[] data = dataToParse.split("\n");
for (int i = 0;i < data.length;i++)
{
String line = data[i];
if(line.startsWith("v "))
{
// Add vertex line to list of vertices
verticesList.add(line);
}
else if(line.startsWith("vt "))
{
textureList.add(line);
}
else if(line.startsWith("vn "))
{
normalList.add(line);
}
else if(line.startsWith("f "))
{
// Add face line to faces list
triangulate(line);
}
}
}
public void triangulate(String lineToTriangulate)
{
String lineSplit[] = lineToTriangulate.split("\s+");
if(lineSplit.length > 4)
{
String line1="";
String line2="";
if (lineToTriangulate.contains("/"))
{
line1 = lineSplit[0] + " " + lineSplit[1].split("/")[0] + " " + lineSplit[2].split("/")[0] + " " + lineSplit[3].split("/")[0];
line2 = lineSplit[0] + " " + lineSplit[1].split("/")[0] + " " + lineSplit[2].split("/")[0] + " " + lineSplit[4].split("/")[0];
}
else
{
line1 = lineSplit[0] + " " + lineSplit[1] + " " + lineSplit[2] + " " + lineSplit[3];
line2 = lineSplit[0] + " " + lineSplit[1] + " " + lineSplit[2] + " " + lineSplit[4];
}
facesVertexList.add(line1);
facesVertexList.add(line2);
}
else
{
if(lineToTriangulate.contains("/"))
{
String[] splitElement1 = lineSplit[1].split("/");
String[] splitElement2 = lineSplit[2].split("/");
String[] splitElement3 = lineSplit[3].split("/");
String line = lineSplit[0] + " " + splitElement1[0] + " " + splitElement2[0] + " " + splitElement3[0];
facesVertexList.add(line);
line = lineSplit[0] + " " + splitElement1[1] + " " + splitElement2[1] + " " + splitElement3[1];
facesTextureList.add(line);
line = lineSplit[0] + " " + splitElement1[2] + " " + splitElement2[2] + " " + splitElement3[2];
facesNormalList.add(line);
}
else
{
facesVertexList.add(lineToTriangulate);
}
}
}
public void draw(float scratch[],float zoom){
int position = GLES30.glGetAttribLocation(program, "position");
GLES30.glEnableVertexAttribArray(position);
GLES30.glVertexAttribPointer(position, 3, GLES30.GL_FLOAT, false, 3 * 4, verticesBuffer);
int mTextureUniformHandle = GLES30.glGetUniformLocation(program, "uTexture");
int mTextureCoordinateHandle = GLES30.glGetAttribLocation(program, "vTexCoordinate");
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glUniform1i(mTextureUniformHandle, 0);
GLES30.glEnableVertexAttribArray(mTextureCoordinateHandle);
GLES30.glVertexAttribPointer(mTextureCoordinateHandle, 2, GLES30.GL_FLOAT, false, 2*4, textureBuffer);
int normalHandle = GLES30.glGetAttribLocation(program,"normal");
GLES30.glEnableVertexAttribArray(normalHandle);
GLES30.glVertexAttribPointer(normalHandle,3,GLES30.GL_FLOAT,false,3*4,normalBuffer);
float[] projectionMatrix = new float[16];
float[] viewMatrix = new float[16];
float[] productMatrix = new float[16];
Matrix.frustumM(projectionMatrix, 0,
-1, 1,
-1, 1,
1, 11);
Matrix.setLookAtM(viewMatrix, 0,
0, 0, zoom,
0, 0, 0,
0, 1, 0);
Matrix.multiplyMM(productMatrix, 0,
projectionMatrix, 0,
viewMatrix, 0);
float[] finalMatrix = new float[16];
Matrix.multiplyMM(finalMatrix, 0,
productMatrix, 0,
scratch, 0);
Matrix.rotateM(finalMatrix, 0, 180, 0.0f, 1.0f, 0.0f);
int matrix = GLES30.glGetUniformLocation(program, "matrix");
GLES30.glUniform1i(matrix,0);
//GLES30.glUniformMatrix4fv(matrix, 1, false, productMatrix, 0);
GLES30.glUniformMatrix4fv(matrix, 1, false, finalMatrix, 0);
int size = facesVertexBuffer.remaining();
GLES30.glEnable(GLES30.GL_CULL_FACE);
GLES30.glCullFace(GLES30.GL_BACK);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES,0,size);
GLES30.glDisableVertexAttribArray(position);
GLES30.glDisableVertexAttribArray(mTextureCoordinateHandle);
}
public void loadTexture()
{
GLES30.glGenTextures(1, textureHandle,0);
if (textureHandle[0] == 0)
{
throw new RuntimeException("Error generating texture name.");
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = true; // No pre-scaling
Bitmap bitmap = BitmapFactory.decodeResource(contextMeshLoader.getResources(), R.raw.pngface, options);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureHandle[0]);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
}
再次感谢。
更新
对于float,是copy/paste的错误。
对于我的显示问题,我找到了解决方案。
我只需要在 onDrawFrame 方法中添加。
GLES30.glEnable(GLES30.GL_DEPTH_TEST);
现在,我的网格和他的贴图可以正确显示了。
感谢您的帮助。
你的假设是错误的。相同的顶点坐标可以关联到不同的纹理坐标。
下面的代码创建了一个新的纹理坐标数组,它有和顶点坐标数组一样多的项目。只有当每个顶点坐标都与恰好 1 个纹理坐标相关联时,这才有效。文件中有 7536 个纹理坐标和 7366 个顶点坐标。
public void parseTexture()
{
int size = facesVertexBuffer.remaining();
System.out.println("size " + size);
for(int i = 0; i < size;i++)
{
int faceVertex = facesVertexBuffer.get(i);
int faceTexture = facesTextureBuffer.get(i);
float a = textureBufferTemp.get((faceTexture)*2);
float b = -textureBufferTemp.get(((faceTexture)*2)+1);
textureBuffer.put((faceVertex*2),a);
textureBuffer.put(((faceVertex)*2)+1,b);
}
textureBuffer.position(0);
System.out.println("end parse texture");
}
如果顶点坐标和纹理坐标有不同的索引,那么顶点位置必须是"duplicated"。
顶点坐标及其属性(如纹理坐标)形成一个数据记录。您可以将 3D 顶点坐标和 2D 纹理坐标想象成一个 5D 坐标。
参见 Rendering meshes with multiple indices。
每个顶点位置的顶点属性构成一组数据。这意味着您必须创建顶点坐标和纹理坐标的元组。
假设您有一个这样的 .obj 文件:
v -1 -1 -1
v 1 -1 -1
v -1 1 -1
v 1 1 -1
v -1 -1 1
v 1 -1 1
v -1 1 1
v 1 1 1
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vn -1 0 0
vn 0 -1 0
vn 0 0 -1
vn 1 0 0
vn 0 1 0
vn 0 0 1
f 3/1/1 1/2/1 5/4/1 7/3/1
f 1/1/2 2/2/2 3/4/2 6/3/2
f 3/1/3 4/2/3 2/4/3 1/3/3
f 2/1/4 4/2/4 8/4/4 6/3/4
f 4/1/5 3/2/5 7/4/5 8/3/5
f 5/1/6 6/2/6 8/4/6 7/3/6
从这里你必须找到面规范中使用的顶点坐标、纹理纹理坐标和法向量索引的所有组合:
0 : 3/1/1
1 : 1/2/1
2 : 5/4/1
3 : 7/3/1
4 : 1/1/2
5 : 2/2/2
6 : 3/4/2
7 : 6/3/2
8 : ...
然后你必须创建一个对应于组合索引数组的顶点坐标、纹理坐标和法向量数组。
顶点坐标及其属性既可以组合成一个数组作为数据集,也可以组合成三个属性数相等的数组:
index vx vy vz u v nx ny nz
0 : -1 1 -1 0 0 -1 0 0
1 : -1 -1 -1 0 1 -1 0 0
2 : -1 -1 1 1 1 -1 0 0
3 : -1 1 1 1 0 -1 0 0
4 : -1 -1 -1 0 0 0 -1 0
5 : 1 -1 -1 0 1 0 -1 0
6 : -1 1 -1 1 1 0 -1 0
7 : 1 -1 1 1 0 0 -1 0
8 : ...
另外请注意,您的应用程序中用于索引的数据类型 short
的范围为 [-32768, 32767]。这对于这个模型来说已经足够大了,但是更大模型的索引数量将超过这个限制。
最简单的解决方法是创建一个三角形基元数组。完全跳过索引缓冲区并使用 GLES30.glDrawArrays()
绘制网格。
红色临时缓冲区中的顶点坐标和纹理坐标。
ByteBuffer bufferVTemp = ByteBuffer.allocateDirect(verticesList.size() * 2 * 4);
bufferVTemp.order(ByteOrder.nativeOrder());
verticesBufferTemp = bufferVTemp.asFloatBuffer();
ByteBuffer bufferTTemp = ByteBuffer.allocateDirect(textureList.size() * 2 * 4);
bufferTTemp.order(ByteOrder.nativeOrder());
textureBufferTemp = bufferTTemp.asFloatBuffer();
for(String vertex: verticesList) {
String coords[] = vertex.split(" "); // Split by space
float x = Float.parseFloat(coords[1]);
float y = Float.parseFloat(coords[2]);
float z = Float.parseFloat(coords[3]);
verticesBufferTemp.put(x);
verticesBufferTemp.put(y);
verticesBufferTemp.put(z);
}
verticesBufferTemp.position(0);
for (String texture: textureList)
{
String textureIndices[] = texture.split("\s+");
float texture1 = Float.parseFloat(textureIndices[1]);
float texture2 = Float.parseFloat(textureIndices[2]);
textureBufferTemp.put(texture1);
textureBufferTemp.put(texture2);
}
textureBufferTemp.position(0);
然后创建一个 tringles 数组
ByteBuffer bufferV = ByteBuffer.allocateDirect(facesVertexBuffer.size() * 3 * 4);
bufferV.order(ByteOrder.nativeOrder());
verticesBuffer = bufferV.asFloatBuffer();
ByteBuffer bufferT = ByteBuffer.allocateDirect(facesVertexBuffer.size() * 2 * 4);
bufferT.order(ByteOrder.nativeOrder());
textureBuffer = bufferT.asFloatBuffer();
int size = facesVertexBuffer.remaining();
System.out.println("size " + size);
for(int i = 0; i < size;i++)
{
int faceVertex = facesVertexBuffer.get(i);
int faceTexture = facesTextureBuffer.get(i);
float x = verticesBufferTemp.get((faceVertex)*2);
float y = verticesBufferTemp.get(((faceVertex)*2)+1);
float z = verticesBufferTemp.get(((faceVertex)*2)+1);
verticesBuffer.put( i*3, x);
verticesBuffer.put( i*3+1, y);
verticesBuffer.put( i*3+2), z);
float u = textureBufferTemp.get((faceTexture)*2);
float v = -textureBufferTemp.get(((faceTexture)*2)+1);
textureBuffer.put( i*2, u);
textureBuffer.put( i*2+1, v);
}
verticesBuffer.position(0);
textureBuffer.position(0);
按GLES30.glDrawArrays()
绘制网格:
GLES30.glDrawElements(GLES30.GL_TRIANGLES, 0, facesVertexBuffer.size());
我正在使用 openglES 3.0 开发一个 Android 应用程序,我想创建一个 obj 加载器系统并显示一个带有纹理的 3D 模型。 我的代码正确显示了没有纹理的 3D 网格。 如果我尝试添加纹理,它会显示纹理并且纹理的某些部分将是空三角形。
示例:
我的带有纹理的网格
我找不到问题所在。
我的片段着色器
precision mediump float;
uniform vec4 vColor;
uniform sampler2D uTexture;
varying vec2 oTexCoordinate;
void main() {
gl_FragColor = texture2D(uTexture, oTexCoordinate);
//gl_FragColor = vec4(1, 0.5, 0, 1.0);
}
我的顶点着色器
attribute vec4 position;
uniform mat4 matrix;
attribute vec2 vTexCoordinate;
varying vec2 oTexCoordinate;
void main() {
oTexCoordinate = vTexCoordinate;
gl_Position = matrix * position;
}
提前致谢。
更新:
谢谢。
我已经更改了我的代码以适应你的想法,我已经激活了面部剔除并为法线添加了一个缓冲区,我暂时不用它。我把我的新代码放在下面。 但是问题并没有完全解决,见下图
我想我需要将法线信息传递给着色器,但我不确定如何正确执行或是否是解决方案。
public class MeshLoader {
private int program;
private List<String> facesVertexList;
private List<String> facesTextureList;
private List<String> facesNormalList;
private List<String> verticesList;
private List<String> textureList;
private List<String> normalList;
private FloatBuffer verticesBuffer;
private FloatBuffer verticesBufferTemp;
private FloatBuffer facesVertexBuffer;
private FloatBuffer facesTextureBuffer;
private FloatBuffer facesNormalBuffer;
private FloatBuffer textureBuffer;
private FloatBuffer textureBufferTemp;
private FloatBuffer normalBuffer;
private FloatBuffer normalBufferTemp;
private Context contextMeshLoader;
final int[] textureHandle = new int[1];
public MeshLoader(Context context) {
contextMeshLoader = context;
textureList = new LinkedList<>();
verticesList = new LinkedList<>();
normalList = new LinkedList<>();
facesVertexList = new LinkedList<>();
facesTextureList = new LinkedList<>();
facesNormalList = new LinkedList<>();
openObjFile(0);
String vertexShaderCode = "";
try{
InputStream vertexShaderStream = context.getResources().openRawResource(R.raw.vertex_shader);
vertexShaderCode = IOUtils.toString(vertexShaderStream, Charset.defaultCharset());
vertexShaderStream.close();
}
catch (Exception e){
Log.e("MeshReaderActivity", "Error reading vertex shader", e);
}
String fragmentShaderCode = "";
try{
InputStream fragmentShaderStream = context.getResources().openRawResource(R.raw.fragment_shader);
fragmentShaderCode = IOUtils.toString(fragmentShaderStream, Charset.defaultCharset());
fragmentShaderStream.close();
}
catch(Exception e){
Log.e("MeshReaderActivity", "Error reading fragment shader", e);
}
int vertexShader = GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER);
GLES30.glShaderSource(vertexShader, vertexShaderCode);
int fragmentShader = GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER);
GLES30.glShaderSource(fragmentShader, fragmentShaderCode);
GLES30.glCompileShader(vertexShader);
GLES30.glCompileShader(fragmentShader);
program = GLES30.glCreateProgram();
GLES30.glAttachShader(program, vertexShader);
GLES30.glAttachShader(program, fragmentShader);
GLES30.glLinkProgram(program);
GLES30.glUseProgram(program);
}
public void openObjFile(int value)
{
InputStream is;
value = 0;
if(value == 0)
is = contextMeshLoader.getResources().openRawResource(R.raw.objface);
else
is = contextMeshLoader.getResources().openRawResource(R.raw.objship);
if(verticesBufferTemp != null)
verticesBufferTemp.clear();
if(facesVertexBuffer != null)
facesVertexBuffer.clear();
if(textureBuffer != null)
textureBuffer.clear();
if(verticesList != null)
verticesList.clear();
if(facesVertexList != null)
facesVertexList.clear();
if(textureList != null)
textureList.clear();
try{
byte[] buffer = new byte[is.available()];
is.read(buffer);
String data = new String(buffer);
parseData(data);
ByteBuffer buffer2 = ByteBuffer.allocateDirect(facesVertexList.size() * 3 * 4);
buffer2.order(ByteOrder.nativeOrder());
facesVertexBuffer = buffer2.asFloatBuffer();
ByteBuffer buffer3 = ByteBuffer.allocateDirect(facesTextureList.size() * 3 * 4);
buffer3.order(ByteOrder.nativeOrder());
facesTextureBuffer = buffer3.asFloatBuffer();
ByteBuffer buffer6 = ByteBuffer.allocateDirect(facesTextureList.size() * 3 * 4);
buffer6.order(ByteOrder.nativeOrder());
facesNormalBuffer = buffer6.asFloatBuffer();
for(String face: facesVertexList) {
String vertexIndices[] = face.split("\s+");
float vertex1 = Float.parseFloat(vertexIndices[1]);
float vertex2 = Float.parseFloat(vertexIndices[2]);
float vertex3 = Float.parseFloat(vertexIndices[3]);
facesVertexBuffer.put((vertex1 - 1));
facesVertexBuffer.put((vertex2 - 1));
facesVertexBuffer.put((vertex3 - 1));
}
facesVertexBuffer.position(0);
for(String texture: facesTextureList){
String textureIndice[] = texture.split("\s+");
float texture1 = Float.parseFloat(textureIndice[1]);
float texture2 = Float.parseFloat(textureIndice[2]);
float texture3 = Float.parseFloat(textureIndice[3]);
facesTextureBuffer.put((texture1 - 1));
facesTextureBuffer.put((texture2 - 1));
facesTextureBuffer.put((texture3 - 1));
}
facesTextureBuffer.position(0);
for(String normal: facesNormalList) {
String normalIndice[] = normal.split("\s+");
float normal1 = Float.parseFloat(normalIndice[1]);
float normal2 = Float.parseFloat(normalIndice[2]);
float normal3 = Float.parseFloat(normalIndice[3]);
facesNormalBuffer.put((normal1 - 1));
facesNormalBuffer.put((normal2 - 1));
facesNormalBuffer.put((normal3 - 1));
}
facesNormalBuffer.position(0);
ByteBuffer buffer1 = ByteBuffer.allocateDirect(verticesList.size() * 3 * 4);
buffer1.order(ByteOrder.nativeOrder());
verticesBufferTemp = buffer1.asFloatBuffer();
ByteBuffer buffer5 = ByteBuffer.allocateDirect(textureList.size() * 2 * 4);
buffer5.order(ByteOrder.nativeOrder());
textureBufferTemp = buffer5.asFloatBuffer();
ByteBuffer buffer7 = ByteBuffer.allocateDirect(textureList.size() * 3 * 4);
buffer7.order(ByteOrder.nativeOrder());
normalBufferTemp = buffer7.asFloatBuffer();
for(String vertex: verticesList) {
String coords[] = vertex.split("\s+");
float x = Float.parseFloat(coords[1]);
float y = Float.parseFloat(coords[2]);
float z = Float.parseFloat(coords[3]);
verticesBufferTemp.put(x);
verticesBufferTemp.put(y);
verticesBufferTemp.put(z);
}
verticesBufferTemp.position(0);
for (String texture:textureList)
{
String textureIndices[] = texture.split("\s+");
float texture1 = Float.parseFloat(textureIndices[1]);
float texture2 = Float.parseFloat(textureIndices[2]);
textureBufferTemp.put(texture1);
textureBufferTemp.put(texture2);
}
textureBufferTemp.position(0);
for (String normal:normalList)
{
String normalIndices[] = normal.split("\s+");
float normal1 = Float.parseFloat(normalIndices[1]);
float normal2 = Float.parseFloat(normalIndices[2]);
normalBufferTemp.put(normal1);
normalBufferTemp.put(normal2);
}
normalBufferTemp.position(0);
System.out.println("size remaining " + facesVertexBuffer.remaining());
ByteBuffer bufferV = ByteBuffer.allocateDirect(facesVertexBuffer.remaining() * 3 * 4);
bufferV.order(ByteOrder.nativeOrder());
verticesBuffer = bufferV.asFloatBuffer();
ByteBuffer bufferT = ByteBuffer.allocateDirect(facesVertexBuffer.remaining() * 2 * 4);
bufferT.order(ByteOrder.nativeOrder());
textureBuffer = bufferT.asFloatBuffer();
ByteBuffer bufferN = ByteBuffer.allocateDirect(facesVertexBuffer.remaining() * 3 * 4);
bufferN.order(ByteOrder.nativeOrder());
normalBuffer = bufferN.asFloatBuffer();
int size = facesVertexBuffer.remaining();
for(int i = 0; i < size;i++)
{
int faceVertex = Math.round(facesVertexBuffer.get(i)) ;
int faceTexture = Math.round(facesTextureBuffer.get(i));
int faceNormal = Math.round(facesNormalBuffer.get(i));
float x = verticesBufferTemp.get((faceVertex)*3);
float y = verticesBufferTemp.get(((faceVertex)*3)+1);
float z = verticesBufferTemp.get(((faceVertex)*3)+2);
verticesBuffer.put( i*3, x);
verticesBuffer.put( (i*3)+1, y);
verticesBuffer.put( (i*3)+2, z);
float u = textureBufferTemp.get((faceTexture)*2);
float v = -textureBufferTemp.get(((faceTexture)*2)+1);
textureBuffer.put( i*2, u);
textureBuffer.put( (i*2)+1, v);
float xn = normalBufferTemp.get((faceNormal*3));
float yn = normalBufferTemp.get((faceNormal*3)+1);
float zn = normalBufferTemp.get((faceNormal*3)+2);
normalBuffer.put(i*3,xn);
normalBuffer.put((i*3)+1,yn);
normalBuffer.put((i*3)+2,zn);
}
verticesBuffer.position(0);
textureBuffer.position(0);
normalBuffer.position(0);
is.close();
loadTexture();
}
catch (Exception e) {
Log.e("MeshReaderActivity", "Error reading objfile", e);
}
}
public void parseData(String dataToParse)
{
Log.i("parse data method", "parse data method");
String[] data = dataToParse.split("\n");
for (int i = 0;i < data.length;i++)
{
String line = data[i];
if(line.startsWith("v "))
{
// Add vertex line to list of vertices
verticesList.add(line);
}
else if(line.startsWith("vt "))
{
textureList.add(line);
}
else if(line.startsWith("vn "))
{
normalList.add(line);
}
else if(line.startsWith("f "))
{
// Add face line to faces list
triangulate(line);
}
}
}
public void triangulate(String lineToTriangulate)
{
String lineSplit[] = lineToTriangulate.split("\s+");
if(lineSplit.length > 4)
{
String line1="";
String line2="";
if (lineToTriangulate.contains("/"))
{
line1 = lineSplit[0] + " " + lineSplit[1].split("/")[0] + " " + lineSplit[2].split("/")[0] + " " + lineSplit[3].split("/")[0];
line2 = lineSplit[0] + " " + lineSplit[1].split("/")[0] + " " + lineSplit[2].split("/")[0] + " " + lineSplit[4].split("/")[0];
}
else
{
line1 = lineSplit[0] + " " + lineSplit[1] + " " + lineSplit[2] + " " + lineSplit[3];
line2 = lineSplit[0] + " " + lineSplit[1] + " " + lineSplit[2] + " " + lineSplit[4];
}
facesVertexList.add(line1);
facesVertexList.add(line2);
}
else
{
if(lineToTriangulate.contains("/"))
{
String[] splitElement1 = lineSplit[1].split("/");
String[] splitElement2 = lineSplit[2].split("/");
String[] splitElement3 = lineSplit[3].split("/");
String line = lineSplit[0] + " " + splitElement1[0] + " " + splitElement2[0] + " " + splitElement3[0];
facesVertexList.add(line);
line = lineSplit[0] + " " + splitElement1[1] + " " + splitElement2[1] + " " + splitElement3[1];
facesTextureList.add(line);
line = lineSplit[0] + " " + splitElement1[2] + " " + splitElement2[2] + " " + splitElement3[2];
facesNormalList.add(line);
}
else
{
facesVertexList.add(lineToTriangulate);
}
}
}
public void draw(float scratch[],float zoom){
int position = GLES30.glGetAttribLocation(program, "position");
GLES30.glEnableVertexAttribArray(position);
GLES30.glVertexAttribPointer(position, 3, GLES30.GL_FLOAT, false, 3 * 4, verticesBuffer);
int mTextureUniformHandle = GLES30.glGetUniformLocation(program, "uTexture");
int mTextureCoordinateHandle = GLES30.glGetAttribLocation(program, "vTexCoordinate");
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glUniform1i(mTextureUniformHandle, 0);
GLES30.glEnableVertexAttribArray(mTextureCoordinateHandle);
GLES30.glVertexAttribPointer(mTextureCoordinateHandle, 2, GLES30.GL_FLOAT, false, 2*4, textureBuffer);
int normalHandle = GLES30.glGetAttribLocation(program,"normal");
GLES30.glEnableVertexAttribArray(normalHandle);
GLES30.glVertexAttribPointer(normalHandle,3,GLES30.GL_FLOAT,false,3*4,normalBuffer);
float[] projectionMatrix = new float[16];
float[] viewMatrix = new float[16];
float[] productMatrix = new float[16];
Matrix.frustumM(projectionMatrix, 0,
-1, 1,
-1, 1,
1, 11);
Matrix.setLookAtM(viewMatrix, 0,
0, 0, zoom,
0, 0, 0,
0, 1, 0);
Matrix.multiplyMM(productMatrix, 0,
projectionMatrix, 0,
viewMatrix, 0);
float[] finalMatrix = new float[16];
Matrix.multiplyMM(finalMatrix, 0,
productMatrix, 0,
scratch, 0);
Matrix.rotateM(finalMatrix, 0, 180, 0.0f, 1.0f, 0.0f);
int matrix = GLES30.glGetUniformLocation(program, "matrix");
GLES30.glUniform1i(matrix,0);
//GLES30.glUniformMatrix4fv(matrix, 1, false, productMatrix, 0);
GLES30.glUniformMatrix4fv(matrix, 1, false, finalMatrix, 0);
int size = facesVertexBuffer.remaining();
GLES30.glEnable(GLES30.GL_CULL_FACE);
GLES30.glCullFace(GLES30.GL_BACK);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES,0,size);
GLES30.glDisableVertexAttribArray(position);
GLES30.glDisableVertexAttribArray(mTextureCoordinateHandle);
}
public void loadTexture()
{
GLES30.glGenTextures(1, textureHandle,0);
if (textureHandle[0] == 0)
{
throw new RuntimeException("Error generating texture name.");
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = true; // No pre-scaling
Bitmap bitmap = BitmapFactory.decodeResource(contextMeshLoader.getResources(), R.raw.pngface, options);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureHandle[0]);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
}
再次感谢。
更新
对于float,是copy/paste的错误。 对于我的显示问题,我找到了解决方案。 我只需要在 onDrawFrame 方法中添加。
GLES30.glEnable(GLES30.GL_DEPTH_TEST);
现在,我的网格和他的贴图可以正确显示了。
感谢您的帮助。
你的假设是错误的。相同的顶点坐标可以关联到不同的纹理坐标。
下面的代码创建了一个新的纹理坐标数组,它有和顶点坐标数组一样多的项目。只有当每个顶点坐标都与恰好 1 个纹理坐标相关联时,这才有效。文件中有 7536 个纹理坐标和 7366 个顶点坐标。
public void parseTexture() { int size = facesVertexBuffer.remaining(); System.out.println("size " + size); for(int i = 0; i < size;i++) { int faceVertex = facesVertexBuffer.get(i); int faceTexture = facesTextureBuffer.get(i); float a = textureBufferTemp.get((faceTexture)*2); float b = -textureBufferTemp.get(((faceTexture)*2)+1); textureBuffer.put((faceVertex*2),a); textureBuffer.put(((faceVertex)*2)+1,b); } textureBuffer.position(0); System.out.println("end parse texture"); }
如果顶点坐标和纹理坐标有不同的索引,那么顶点位置必须是"duplicated"。 顶点坐标及其属性(如纹理坐标)形成一个数据记录。您可以将 3D 顶点坐标和 2D 纹理坐标想象成一个 5D 坐标。 参见 Rendering meshes with multiple indices。
每个顶点位置的顶点属性构成一组数据。这意味着您必须创建顶点坐标和纹理坐标的元组。
假设您有一个这样的 .obj 文件:
v -1 -1 -1
v 1 -1 -1
v -1 1 -1
v 1 1 -1
v -1 -1 1
v 1 -1 1
v -1 1 1
v 1 1 1
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vn -1 0 0
vn 0 -1 0
vn 0 0 -1
vn 1 0 0
vn 0 1 0
vn 0 0 1
f 3/1/1 1/2/1 5/4/1 7/3/1
f 1/1/2 2/2/2 3/4/2 6/3/2
f 3/1/3 4/2/3 2/4/3 1/3/3
f 2/1/4 4/2/4 8/4/4 6/3/4
f 4/1/5 3/2/5 7/4/5 8/3/5
f 5/1/6 6/2/6 8/4/6 7/3/6
从这里你必须找到面规范中使用的顶点坐标、纹理纹理坐标和法向量索引的所有组合:
0 : 3/1/1
1 : 1/2/1
2 : 5/4/1
3 : 7/3/1
4 : 1/1/2
5 : 2/2/2
6 : 3/4/2
7 : 6/3/2
8 : ...
然后你必须创建一个对应于组合索引数组的顶点坐标、纹理坐标和法向量数组。 顶点坐标及其属性既可以组合成一个数组作为数据集,也可以组合成三个属性数相等的数组:
index vx vy vz u v nx ny nz
0 : -1 1 -1 0 0 -1 0 0
1 : -1 -1 -1 0 1 -1 0 0
2 : -1 -1 1 1 1 -1 0 0
3 : -1 1 1 1 0 -1 0 0
4 : -1 -1 -1 0 0 0 -1 0
5 : 1 -1 -1 0 1 0 -1 0
6 : -1 1 -1 1 1 0 -1 0
7 : 1 -1 1 1 0 0 -1 0
8 : ...
另外请注意,您的应用程序中用于索引的数据类型 short
的范围为 [-32768, 32767]。这对于这个模型来说已经足够大了,但是更大模型的索引数量将超过这个限制。
最简单的解决方法是创建一个三角形基元数组。完全跳过索引缓冲区并使用 GLES30.glDrawArrays()
绘制网格。
红色临时缓冲区中的顶点坐标和纹理坐标。
ByteBuffer bufferVTemp = ByteBuffer.allocateDirect(verticesList.size() * 2 * 4);
bufferVTemp.order(ByteOrder.nativeOrder());
verticesBufferTemp = bufferVTemp.asFloatBuffer();
ByteBuffer bufferTTemp = ByteBuffer.allocateDirect(textureList.size() * 2 * 4);
bufferTTemp.order(ByteOrder.nativeOrder());
textureBufferTemp = bufferTTemp.asFloatBuffer();
for(String vertex: verticesList) {
String coords[] = vertex.split(" "); // Split by space
float x = Float.parseFloat(coords[1]);
float y = Float.parseFloat(coords[2]);
float z = Float.parseFloat(coords[3]);
verticesBufferTemp.put(x);
verticesBufferTemp.put(y);
verticesBufferTemp.put(z);
}
verticesBufferTemp.position(0);
for (String texture: textureList)
{
String textureIndices[] = texture.split("\s+");
float texture1 = Float.parseFloat(textureIndices[1]);
float texture2 = Float.parseFloat(textureIndices[2]);
textureBufferTemp.put(texture1);
textureBufferTemp.put(texture2);
}
textureBufferTemp.position(0);
然后创建一个 tringles 数组
ByteBuffer bufferV = ByteBuffer.allocateDirect(facesVertexBuffer.size() * 3 * 4);
bufferV.order(ByteOrder.nativeOrder());
verticesBuffer = bufferV.asFloatBuffer();
ByteBuffer bufferT = ByteBuffer.allocateDirect(facesVertexBuffer.size() * 2 * 4);
bufferT.order(ByteOrder.nativeOrder());
textureBuffer = bufferT.asFloatBuffer();
int size = facesVertexBuffer.remaining();
System.out.println("size " + size);
for(int i = 0; i < size;i++)
{
int faceVertex = facesVertexBuffer.get(i);
int faceTexture = facesTextureBuffer.get(i);
float x = verticesBufferTemp.get((faceVertex)*2);
float y = verticesBufferTemp.get(((faceVertex)*2)+1);
float z = verticesBufferTemp.get(((faceVertex)*2)+1);
verticesBuffer.put( i*3, x);
verticesBuffer.put( i*3+1, y);
verticesBuffer.put( i*3+2), z);
float u = textureBufferTemp.get((faceTexture)*2);
float v = -textureBufferTemp.get(((faceTexture)*2)+1);
textureBuffer.put( i*2, u);
textureBuffer.put( i*2+1, v);
}
verticesBuffer.position(0);
textureBuffer.position(0);
按GLES30.glDrawArrays()
绘制网格:
GLES30.glDrawElements(GLES30.GL_TRIANGLES, 0, facesVertexBuffer.size());