无法在 OpenGL ES 中渲染简单的三角形

Cannot render simple triangle in OpenGL ES

我正在尝试通过渲染一个简单的三角形开始使用 OpenGL ES。

我使用 Android 和 Java 代码完成所有 EGL 初始化,然后 JNI 调用以便我可以使用 C 进行实际渲染。我已经通过简单地在 glClearColor 命令中更改颜色,然后调用 glClear(然后会出现正确的颜色)来验证所有内容都已正确连接。但是,当我尝试下面的代码时,什么也没有出现,我也不知道为什么。请原谅许多 glGetError 电话。我只是想确定没有调用失败,确实如此。

#include "gl_render.h"
#include "gl_wrapper.h"
#include <math.h>

GLuint programObject;

GLuint LoadShader ( GLenum type, const char *shaderSrc )
{
   GLuint shader;
   GLint compiled;
   GLenum error;

   shader = glCreateShader ( type );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -50;
   }

   glShaderSource ( shader, 1, &shaderSrc, NULL );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -51;
   }

   glCompileShader ( shader );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -52;
   }

   glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -53;
   }

   if ( !compiled ) {
      GLint infoLen = 0;

      glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );

      error = glGetError();
      if (error != GL_NO_ERROR) {
         return -54;
      }

      if ( infoLen > 1 ) {
         char *infoLog = malloc ( sizeof ( char ) * infoLen );

         glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );

         error = glGetError();
         if (error != GL_NO_ERROR) {
            return -55;
         }

         free ( infoLog );
      }

      glDeleteShader ( shader );

      error = glGetError();
      if (error != GL_NO_ERROR) {
         return -56;
      }

      return -2;
   }

   return shader;
}

int on_surface_created() {
   char vShaderStr[] =
      "#version 300 es                          \n"
      "layout(location = 0) in vec4 vPosition;  \n"
      "void main()                              \n"
      "{                                        \n"
      "   gl_Position = vPosition;              \n"
      "}                                        \n";

   char fShaderStr[] =
      "#version 300 es                              \n"
      "precision mediump float;                     \n"
      "out vec4 fragColor;                          \n"
      "void main()                                  \n"
      "{                                            \n"
      "   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );  \n"
      "}                                            \n";

   GLuint vertexShader;
   GLuint fragmentShader;
   GLuint programObject;
   GLint linked;
   GLenum error;

   vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr );

   if (vertexShader < 0) {
      return vertexShader;
   }

   fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr );

   if (fragmentShader < 0) {
      return fragmentShader;
   }

   programObject = glCreateProgram ( );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -57;
   }

   glAttachShader ( programObject, vertexShader );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -58;
   }

   glAttachShader ( programObject, fragmentShader );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -59;
   }

   glLinkProgram ( programObject );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -60;
   }

   glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -61;
   }

   if ( !linked ) {
      GLint infoLen = 0;

      glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );

      error = glGetError();
      if (error != GL_NO_ERROR) {
         return -62;
      }

      if ( infoLen > 1 ) {
         char *infoLog = malloc ( sizeof ( char ) * infoLen );

         glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );

         error = glGetError();
         if (error != GL_NO_ERROR) {
            return -63;
         }

         free ( infoLog );
      }

      glDeleteProgram ( programObject );

      error = glGetError();
      if (error != GL_NO_ERROR) {
         return -64;
      }

      return -2;
   }

   glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -65;
   }

   return 0;
}

int on_draw_frame() {
   GLenum error;

   GLfloat vVertices[] = {  0.0f,  0.5f, 0.0f,
                            -0.5f, -0.5f, 0.0f,
                            0.5f, -0.5f, 0.0f
                         };

   glViewport ( 0, 0, 200, 200 );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -66;
   }

   glClear ( GL_COLOR_BUFFER_BIT );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -67;
   }

   glUseProgram ( programObject );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -68;
   }

   glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -69;
   }

   glEnableVertexAttribArray ( 0 );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -70;
   }

   glDrawArrays ( GL_TRIANGLES, 0, 3 );

   error = glGetError();
   if (error != GL_NO_ERROR) {
      return -71;
   }

   return 0;
}

作为参考,这里是挂接到此的 Java 代码:

package com.example.spike_opengl;

import android.graphics.SurfaceTexture;
import android.opengl.GLUtils;
import android.util.Log;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;

public final class OpenGLRenderer implements Runnable {
    private static final String LOG_TAG = "OpenGL.Worker";
    protected final SurfaceTexture texture;
    private EGL10 egl;
    private EGLDisplay eglDisplay;
    private EGLContext eglContext;
    private EGLSurface eglSurface;

    private boolean running;

    private Worker worker;

    public OpenGLRenderer(SurfaceTexture texture, Worker worker) {
        this.texture = texture;
        this.running = true;
        this.worker = worker;

        Thread thread = new Thread(this);
        thread.start();
    }

    @Override
    public void run() {
        initGL();
        int initResult = worker.onCreate();

        if (initResult != 0) {
            Log.d(LOG_TAG, "OpenGL init FAILED with code" + initResult + ".");
        } else {
            Log.d(LOG_TAG, "OpenGL init OK.");
        }

        while (running) {
            long loopStart = System.currentTimeMillis();
            int drawResult = worker.onDraw();

            if (drawResult != 0) {
                Log.d(LOG_TAG, "OpenGL draw FAILED with code" + drawResult + ".");
            } else {
                if (!egl.eglSwapBuffers(eglDisplay, eglSurface)) {
                    Log.d(LOG_TAG, String.valueOf(egl.eglGetError()));
                }
            }

            long waitDelta = 16 - (System.currentTimeMillis() - loopStart);
            if (waitDelta > 0) {
                try {
                    Thread.sleep(waitDelta);
                } catch (InterruptedException e) {
                }
            }
        }

        deinitGL();
    }

    private void initGL() {
        egl = (EGL10) EGLContext.getEGL();
        eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        if (eglDisplay == EGL10.EGL_NO_DISPLAY) {
            throw new RuntimeException("eglGetDisplay failed");
        }

        int[] version = new int[2];
        if (!egl.eglInitialize(eglDisplay, version)) {
            throw new RuntimeException("eglInitialize failed");
        }

        EGLConfig eglConfig = chooseEglConfig();
        eglContext = createContext(egl, eglDisplay, eglConfig);

        eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, texture, null);

        if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) {
            throw new RuntimeException("GL Error: " + GLUtils.getEGLErrorString(egl.eglGetError()));
        }

        if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
            throw new RuntimeException("GL make current error: " + GLUtils.getEGLErrorString(egl.eglGetError()));
        }
    }

    private void deinitGL() {
        egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        egl.eglDestroySurface(eglDisplay, eglSurface);
        egl.eglDestroyContext(eglDisplay, eglContext);
        egl.eglTerminate(eglDisplay);
        Log.d(LOG_TAG, "OpenGL deinit OK.");
    }

    private EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
        int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
        int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
        return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attribList);
    }

    private EGLConfig chooseEglConfig() {
        int[] configsCount = new int[1];
        EGLConfig[] configs = new EGLConfig[1];
        int[] configSpec = getConfig();

        if (!egl.eglChooseConfig(eglDisplay, configSpec, configs, 1, configsCount)) {
            throw new IllegalArgumentException("Failed to choose config: " + GLUtils.getEGLErrorString(egl.eglGetError()));
        } else if (configsCount[0] > 0) {
            return configs[0];
        }

        return null;
    }

    private int[] getConfig() {
        return new int[]{
                EGL10.EGL_RENDERABLE_TYPE, 4,
                EGL10.EGL_RED_SIZE, 8,
                EGL10.EGL_GREEN_SIZE, 8,
                EGL10.EGL_BLUE_SIZE, 8,
                EGL10.EGL_ALPHA_SIZE, 8,
                EGL10.EGL_DEPTH_SIZE, 16,
                EGL10.EGL_STENCIL_SIZE, 0,
                EGL10.EGL_SAMPLE_BUFFERS, 1,
                EGL10.EGL_SAMPLES, 4,
                EGL10.EGL_NONE
        };
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        running = false;
    }

    public void onDispose() {
        running = false;
    }

    public interface Worker {
        int onCreate();

        int onDraw();
    }
}

谁能告诉我哪里做错了?

变量programObject被声明了两次。

在全局范围内声明一次:

GLuint programObject;

第2次在函数中声明on_surface_created:

int on_surface_created() {

    // ...

    GLuint programObject;

    // ...

    programObject = glCreateProgram ( );

    // ...
}

虽然设置了局部变量,但永远不会设置全局范围内的变量。这导致着色器程序未作为当前渲染状态的一部分安装,在 "on_draw frame":

int on_draw_frame() {

    // ...

    glUseProgram ( programObject );

    // ...
}

只需删除on_surface_created中的局部变量programObject即可解决问题。