简体   繁体   中英

Cannot render simple triangle in OpenGL ES

I'm trying to get started with OpenGL ES by rendering a simple triangle.

I'm using Android with Java code doing all the EGL initialization, then JNI calls so I can use C for the actual rendering. I've already verified that everything is hooked up correctly by simply changing colors in a glClearColor command, then calling glClear (the correct color then appears). However, when I try the below code, nothing appears and I can't figure out why. Please excuse the many glGetError calls. I just wanted to be sure no calls were failing, and they're not.

#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;
}

For reference, here is the Java code that hooks into this:

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();
    }
}

Can anyone tell me where I've gone wrong?

The variable programObject is declared twice.

One time it is declared in global scope:

GLuint programObject;

The 2nd time it is declared in the function on_surface_created :

int on_surface_created() {

    // ...

    GLuint programObject;

    // ...

    programObject = glCreateProgram ( );

    // ...
}

While the local variable is set, the one in global scope is never set. This causes that the shader program is not installed as part of current rendering state, in "on_draw frame":

int on_draw_frame() {

    // ...

    glUseProgram ( programObject );

    // ...
}

Just remove the local variable programObject in on_surface_created , to solve the issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM