本文将会记录笔者在学习 OpenGL ES 过程中,遇到的问题及解决问题的参考办法。同时也提供了一些 Debug 问题的分析思路和技巧。
更新记录以下代码基于 Kotlin 和 C++。
创建 Shader 的大致步骤如下:
// 创建一个 shader,并返回它的 handle // 其中,输入参数 shaderType 为想创建的 shader 类型, // 一般是 GLES30.GL_VERTEX_SHADER 或 GLES30.GL_FRAGMENT_SHADER val shaderHandle = GLES30.glCreateShader(shadeType) // 将预先准备好的源码与该 shader 绑定 GLES30.glShaderSource(shaderHandle, sourceCode) // 编译 shader GLES30.glCompileShader(shaderHandle)
一般,问题容易出现在 GLES30.glCompileShader() 阶段,极少情况下,GLES30.glCreateShader() 时会发生异常。
shader 的 handle 一定是大于 0 的,可以用这一点来判断 glCreateShader() 是否发生异常。可以用 GLES30.glGetShaderiv()1 和 GLES30.glGetShaderInfoLog()2 来判断 glCompileShader() 是否发生异常。
基于以上考虑,笔者将创建 shader 的过程封装成一个函数: 创建 program 的大致步骤如下: 这里的三种方法调用都有可能发生错误。 OpenGL 的接口调用必须要在 EGL 上下文创建完成后进行。例如,如果是使用 GLSurface,那应该是在 GLSurface.Render.onSurfaceCreated() 回调方法之后执行 OpenGL *** 作。如果是自定义 Render,谨记不要在 Render 的构造函数中调用。 OpenGL 提供的 状态/信息/错误 查询接口,尽量不要在线上环境使用。因为这涉及到 CPU 与 GPU 间通信,太过频繁容易影响到性能。 如果是 native 发生了 Crash,原因有很多,例如空指针。建议借助 llvm-symbolizer 工具定位出错行。 笔者这里遇到的比较奇葩,是非 void 函数,未返回值发生的 Crash。举例: 让我很费解的是,这种错误应该编译期就报出的,不知道为什么到运行期才报错。个人认为这是 NDK 的一个 Bug。 glGetShaderiv ↩︎ glGetShaderInfoLog ↩︎ 欢迎分享,转载请注明来源:内存溢出
fun createShader(shaderType: Int, shaderCode: String): Int {
val shaderTypeStr = when (shaderType) {
GLES30.GL_VERTEX_SHADER -> "Vertex Shader"
GLES30.GL_FRAGMENT_SHADER -> "Fragment Shader"
else -> "Unsupported type"
}
// !!!注意!!!DEBUG 为笔者自己加的调试开关,在该函数外定义
if (DEBUG) {
Log.d(TAG, "create [$shaderTypeStr] source code $shaderCode")
}
// 如果创建的不是顶点着色器或片段着色器,打印一条警示 Log
if (type != GLES30.GL_VERTEX_SHADER && type != GLES30.GL_FRAGMENT_SHADER) {
Log.w(TAG, "create shader maybe failed: unsupported shader type, your input $type")
}
return GLES30.glCreateShader(shaderType).also {
if (DEBUG) {
Log.d(TAG, "created [shaderTypeStr] handle: $it")
}
GLES30.glShaderSource(it, shaderCode)
GLES30.glCompileShader(it)
if (DEBUG) {
// 检查 shader 编译的状态
val compileStatus = IntBuffer.allocate(1)
GLES30.glGetShaderiv(it, GLES30.GL_COMPILE_STATUS, compileStatus)
Log.d(
TAG,
"compile [$shaderTypeStr] status: ${if (compileStatus[0] == GLES30.GL_TRUE) "success" else "failed"}"
)
// 如果编译失败,打印出错误信息
if (compileStatus[0] == GLES30.GL_FALSE) {
Log.e(TAG, "compile [$shaderTypeStr] failed, info: ${GLES30.glGetShaderInfoLog(it)}")
}
}
}
}
二、检查 program 创建状态
// 创建并得到一个 program handle
val programHandle = GLES30.glCreateProgram()
// 绑定顶点着色器
GLES30.glAttachShader(programHandle, vertexShaderHandle)
// 绑定片段着色器
GLES30.glAttachShader(programHandle, fragmentShaderHandle)
// 链接该 program
GLES30.gllinkProgram(programHandle)
创建 program 也可以封装成一个函数,所以可以这么做:fun createProgram(vertex: Int, fragment: Int): Int = GLES30.glCreateProgram().also {
// 检验输入参数的合法性
if (vertex <= 0 || fragment <= 0) {
Log.w(TAG, "invalid argument: vertex $vertex, fragment $fragment")
}
GLES30.glAttachShader(it, vertex)
if (DEBUG) {
checkGLError("Attach Vertex Shader")
}
GLES30.glAttachShader(it, fragment)
if (DEBUG) {
checkGLError("Attach Fragment Shader")
}
GLES30.gllinkProgram(it)
if (DEBUG) {
val linkStatus = IntBuffer.allocate(1)
GLES30.glGetProgramiv(it, GLES30.GL_link_STATUS, linkStatus)
if (linkStatus[0] == GLES30.GL_TRUE) {
Log.d(TAG, "link program success!")
} else {
Log.e(
TAG,
"link program failed! info: ${GLES30.glGetProgramInfoLog(it)}"
)
}
}
}
fun checkGLError(what: String) {
Log.w(TAG, "[$what] error code: ${GLES30.glGetError()}")
}
问题列表
1、确认 shader 代码正常,但编译 shader、创建 program 失败
unsigned int createframebuffer() {
// ......
// 做了一些列 *** 作
// 最后没有返回值
}
评论列表(0条)