我正在写一个应用程序在iOS上允许绘制自由风格(使用手指)和在屏幕上绘制图像。我使用OpenGL ES来实现。我有两个功能,一个是绘制自由样式,一个是绘制纹理
-代码绘制自由样式
- (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个变量a_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用于片段着色器。根据这些标志来知道绘制自由样式或纹理。
但如果我独立运行,每次只有一种类型(如果运行自由绘制样式,注释代码配置顶点着色器和片段着色器文件上的绘制纹理,反之亦然)它可以像我想要的那样绘制。如果我将它们组合在一起,它不仅不能绘制,还会使应用程序崩溃,因为
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);我是OpenGL ES和GLSL语言的新手,所以我不确定我这样设置标志的想法是对还是错。有人能帮我吗?
发布于 2014-12-17 05:16:41
所以你为什么不在其中的一个上构建两个独立的着色器程序和useProgram(),而不是将标志值发送到GL,并在顶点中进行昂贵的条件分支,特别是片段着色器?
发布于 2014-12-17 06:03:34
属性是每个顶点的,制服是每个着色器程序的。
如果您只为一个属性提供了一个值,然后要求OpenGL绘制100个点,那么您可能会看到崩溃。在这种情况下,当OpenGL试图获取顶点2-100的属性时,它将进行越界数组访问。
使用两个独立的程序会更正常。条件在GPU上是非常昂贵的,因为GPU在处理多个片段时试图维护一个程序计数器。它们是SIMD设备。任何时候,当两个相邻片段之间的if计算结果不同时,您可能会降低并行度。因此,习惯用法是在可能的情况下不使用if语句。
如果你切换到uniform,你很有可能不会因为没有并行而损失任何性能,因为路径永远不会分叉。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以使两个调用完全相同,并可能将它们排除在调用之外,以确保您最终不会总是执行两个样本而不是一个样本。
发布于 2014-12-17 13:16:01
之前发布的答案解释了某些部分,但所有答案中都缺少一个重要的部分。在绘图调用中指定应用于所有顶点的单个属性值是完全合法的。您在这里所做的基本上是有效的:
glVertexAttrib1f(a_ver_flag_drawing_type, 1.0f);直接的问题是不久之后的这个调用:
glEnableVertexAttribArray(a_ver_flag_drawing_type);指定顶点属性值的方法主要有两种:
通过使用glVertexAttribPointer().指定的数组中的glVertexAttrib1f().
您可以通过启用/禁用数组来选择将这两个选项中的哪一个用于任何给定属性,这是通过调用glEnableVertexAttribArray()/glDisableVertexAttribArray()来完成的。
在发布的代码中,顶点属性仅指定为当前值,但随后启用该属性以使用glEnableVertexAttribArray()从数组中获取。这种冲突导致了崩溃,因为属性值将从从未指定的数组中提取。要使用指定的当前值,只需将调用更改为:
glDisableVertexAttribArray(a_ver_flag_drawing_type);或者,如果从未启用该数组,则可以完全省略调用。但以防代码的另一部分可能启用了它,显式禁用它会更安全。
顺便说一下,来自第一个绘制函数的以下语句序列看起来也很可疑。glUniform*()在活动程序上设置值,因此这将在先前活动程序上设置一个值,而不是在第二条语句中指定的值。如果要在新程序上设置值,则必须颠倒语句的顺序。
glUniform1f(u_fra_flag_drawing_type, 0.0);
glUseProgram(program[PROGRAM_POINT].id);总的来说,我认为至少有两种方法比所选择的方法更好:
均匀布尔型v_use_texture;
然后测试就变成了:
如果(v_use_texture) {
获取统一位置的过程与之前相同,您可以使用以下选项之一设置该值,该值在顶点着色器和片段着色器中都可用:
glUniform1i(loc,0);glUniform1i(loc,1);
https://stackoverflow.com/questions/27496144
复制相似问题