java - OpenGL ES 纹理未正确渲染
问题描述
我正在使用 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();
}
}
再次感谢。
更新
对于浮动,这是复制/粘贴错误。对于我的显示问题,我找到了解决方案。我只需要添加 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"); }
如果顶点坐标和纹理坐标有不同的索引,则必须“复制”顶点位置。顶点坐标及其属性(如纹理坐标)形成数据记录。您可以将 3D 顶点坐标和 2D 纹理坐标想象为单个 5D 坐标。请参阅使用多个索引渲染网格。
每个顶点位置的顶点属性形成一组数据。这意味着您必须创建顶点坐标和纹理坐标的元组。
假设您有一个这样的.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);
然后创建一个三角形数组
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());
推荐阅读
- google-cloud-platform - 在 GCP API 指标页面上访问、查看和修改过滤器的 IAM 权限
- github - 将所有 github repo 备份到 bitbucket
- javascript - thenable 函数。应用了不同的返回类型 on call 或 `await` /`then`
- asp.net-core - ASP.Net Identity SSO 多应用
- rstudio - 为什么 R studio 不读取 R 代码?
- google-cloud-platform - Terraform - 同时将多个文件复制到存储桶创建存储桶
- python - 在 pandas 的下游作业中通过 Athena 读取分区数据
- google-cloud-platform - 从触发器开始数据准备作业后运行 SQL 更新语句 - Google Cloud
- android-jetpack-compose - 如何在横向模式下预览 Compose 布局
- java - Apache Beam Pipeline 无法将数据插入 BigQuery。工作流失败