OpenGL ES的glFragColor取决于iOS上的片段着色器的条件

我在iOS上编写应用程序允许在屏幕上绘制自由样式(使用手指)和绘制图像。 我使用OpenGL ES来实现。 我有2个function,一个是画自由风格,一个是画纹理

—代码绘制自由风格

- (void)drawFreeStyle:(NSMutableArray *)pointArray { //Prepare vertex data ..... // Load data to the Vertex Buffer Object glBindBuffer(GL_ARRAY_BUFFER, vboId); glBufferData(GL_ARRAY_BUFFER, vertexCount*2*sizeof(GLfloat), vertexBuffer, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(ATTRIB_VERTEX); glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, 0); **GLuint a_ver_flag_drawing_type = glGetAttribLocation(program[PROGRAM_POINT].id, "a_drawingType"); glVertexAttrib1f(a_ver_flag_drawing_type, 0.0f); GLuint u_fra_flag_drawing_type = glGetUniformLocation(program[PROGRAM_POINT].id, "v_drawing_type"); glUniform1f(u_fra_flag_drawing_type, 0.0);** glUseProgram(program[PROGRAM_POINT].id); glDrawArrays(GL_POINTS, 0, (int)vertexCount); // Display the buffer glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER]; } 

—代码绘制纹理

 - (void)drawTexture:(UIImage *)image atRect:(CGRect)rect { GLuint a_ver_flag_drawing_type = glGetAttribLocation(program[PROGRAM_POINT].id, "a_drawingType"); GLuint u_fra_flag_drawing_type = glGetUniformLocation(program[PROGRAM_POINT].id, "v_drawing_type"); GLuint a_position_location = glGetAttribLocation(program[PROGRAM_POINT].id, "a_Position"); GLuint a_texture_coordinates_location = glGetAttribLocation(program[PROGRAM_POINT].id, "a_TextureCoordinates"); GLuint u_texture_unit_location = glGetUniformLocation(program[PROGRAM_POINT].id, "u_TextureUnit"); glUseProgram(PROGRAM_POINT); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texName); glUniform1i(u_texture_unit_location, 0); glUniform1f(u_fra_flag_drawing_type, 1.0); const float textrect[] = {-1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f}; glBindBuffer(GL_ARRAY_BUFFER, vboId); glBufferData(GL_ARRAY_BUFFER, sizeof(textrect), textrect, GL_STATIC_DRAW); glVertexAttrib1f(a_ver_flag_drawing_type, 1.0f); glVertexAttribPointer(a_position_location, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(0)); glVertexAttribPointer(a_texture_coordinates_location, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); glEnableVertexAttribArray(a_ver_flag_drawing_type); glEnableVertexAttribArray(a_position_location); glEnableVertexAttribArray(a_texture_coordinates_location); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER]; } 

注意2个variablesa_ver_flag_drawing_type (属性)和u_fra_flag_drawing_type (统一)。 它们用于在顶点着色器和片段着色器上设置标志,以确定在两个文件上绘制自由样式或纹理

—顶点着色器

 //Flag attribute lowp float a_drawingType; //For drawing attribute vec4 inVertex; uniform mat4 MVP; uniform float pointSize; uniform lowp vec4 vertexColor; varying lowp vec4 color; //For texture attribute vec4 a_Position; attribute vec2 a_TextureCoordinates; varying vec2 v_TextureCoordinates; void main() { if (abs(a_drawingType - 1.0) < 0.0001) { //Draw texture v_TextureCoordinates = a_TextureCoordinates; gl_Position = a_Position; } else { //Draw free style gl_Position = MVP * inVertex; gl_PointSize = pointSize; color = vertexColor; } } 

—片段着色器

 precision mediump float; uniform sampler2D texture; varying lowp vec4 color; uniform sampler2D u_TextureUnit; varying vec2 v_TextureCoordinates; uniform lowp float v_drawing_type; void main() { if (abs(v_drawing_type - 1.0) < 0.0001) { //Draw texture gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates); } else { //Drawing free style gl_FragColor = color * texture2D(texture, gl_PointCoord); } } 

我的想法是在绘图时从绘图代码中设置这些标志。 属性a_drawingType用于顶点着色器,而统一v_drawing_type用于片段着色器。 根据这些标志来了解绘制自由样式或纹理。

但是,如果我独立运行,每次只有一种types(如果运行绘制自由风格,注释代码configuration绘制纹理顶点着色器和片段着色器文件,反之亦然),它可以绘制我想要的。 如果我把它们结合起来,它不但不能画,而且会导致应用程序崩溃

 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 

我是OpenGL ES和GLSL语言的新手,所以我不确定自己想设置这样的标志是对还是错。 谁能帮我

那么你为什么不build立2个独立的着色器程序,并在其中一个上使用Program(),而不是将标志值发送给GL,并在顶点和特别是片段着色器中制作昂贵的条件分支?

属性是每个顶点,制服是每个着色器程序。

如果您只为属性提供了一个值,那么您可能会看到崩溃,然后要求OpenGL绘制100点。 在这种情况下,当OpenGL试图获取顶点2-100的属性时,它将会进行一个超出边界的数组访问。

使用两个单独的程序更为正常。 GPU上的条件非常昂贵,因为GPU在处理多个片段时尝试维护一个程序计数器。 他们是SIMD单位。 任何时候if两个相邻片段之间的差异评估不同,则可能会降低并行性。 因此,这个习语在可能的情况下不要使用if语句。

如果uniform ,很有可能不会因为没有平行性而失去任何performance,因为path永远不会分道扬parallel。 您的GLSL编译器甚至可以足够聪明,每次重置常量时重新编译着色器,从而有效地执行常量折叠。 但是你每次都要为重新编译付费。

如果您只有两个程序并在它们之间切换,则不会支付重新编译费用。

在这种情况下,这不是完全相关的,但是例如你也经常看到如下的代码:

 if (abs(v_drawing_type - 1.0) < 0.0001) { //Draw texture gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates); } else { //Drawing free style gl_FragColor = color * texture2D(texture, gl_PointCoord); } 

写得更像:

 gl_FragColor = mix(texture2D(u_TextureUnit, v_TextureCoordinates), color * texture2D(texture, gl_PointCoord), v_drawing_type); 

…因为这完全避免了条件。 在这种情况下,您需要调整texture2D ,使得两个调用都是相同的,并且可能将这些调用排除在调用之外,以确保您最终不会总是执行两个样本而不是一个样本。

之前发布的答案解释了某些片断,但其中一个重要的部分是缺失的。 指定应用于绘图调用中所有顶点的单个属性值是完全合法的。 你在这里做的基本上是有效的:

 glVertexAttrib1f(a_ver_flag_drawing_type, 1.0f); 

直接的问题是紧随其后的这个呼叫:

 glEnableVertexAttribArray(a_ver_flag_drawing_type); 

有两种主要的方法来指定一个顶点属性的值:

  1. 使用glVertexAttrib1f()指定的当前值。
  2. 使用glVertexAttribPointer()指定的数组中的值。

您可以通过调用glEnableVertexAttribArray() / glDisableVertexAttribArray()来启用/禁用数组,从而select用于任何给定属性的两个选项中的哪一个。

在发布的代码中,顶点属性被指定为仅当前值,但是该属性然后被启用以从具有glEnableVertexAttribArray()的数组中获取。 这个冲突导致了崩溃,因为属性值是从一个从未指定的数组中取得的。 要使用指定的当前值,只需将呼叫更改为:

 glDisableVertexAttribArray(a_ver_flag_drawing_type); 

或者,如果数组从未被启用,则该呼叫可以被完全排除。 但是为了防止代码的另一部分可能已经启用它,明确禁用它会更安全。

作为一个方面,从第一个绘制函数的下面的语句序列也看起来可疑。 glUniform*()在激活的程序中设置了一个值,所以它将在前一个激活的程序上设置一个值,而不是在第二个语句中指定的值。 如果要设置新程序的值,则必须颠倒语句的顺序。

 glUniform1f(u_fra_flag_drawing_type, 0.0); glUseProgram(program[PROGRAM_POINT].id); 

总的来说,我认为至less有两种方法比所select的更好:

  1. 对两种不同types的渲染使用单独的着色器程序。 虽然使用可切换行为的单一程序是一个有效的select,但它看起来很模糊,使用单独的程序似乎更清晰。
  2. 如果你想坚持一个单一的程序,使用一个统一的做切换,而不是使用属性和制服。 你可以使用你已经拥有的那个,但是你也可以使它成为一个布尔值。 所以在顶点和片段着色器中,使用相同的统一声明:

     uniform bool v_use_texture; 

    然后testing成为:

     if (v_use_texture) { 

    获得统一的位置与以前相同,您可以使用以下之一来设置顶点和片段着色器中可用的值:

     glUniform1i(loc, 0); glUniform1i(loc, 1); 

我发现这个问题,只需将variablesa_drawingType从属性更改为统一,然后使用glGetUniformLocation和glUniform1f来获取索引和传递值。 我认为属性将通过任何顶点,所以使用统一传递一次。