Почему этот шейдер OpenGL ES 2.0 не работает с моим VBO на iOS?
Если кто-нибудь может пролить свет на то, что здесь происходит неправильно, возможно, неправильный порядок команд gl или какая-то другая несовместимая последовательность команд, я был бы чрезвычайно благодарен за вашу помощь. Я пытался заставить этот код работать весь день без успеха, несмотря на многочисленные исследования Google и изучение примеров в "руководстве по программированию OpenGL ES 2.0".
Я пытаюсь использовать объект Vertex Buffer и пользовательские шейдеры в OpenGL ES 2.0 на iPhone. Я пытаюсь перемежать вершину данные из серии пользовательских структур следующего типа:
typedef struct {
float x, y; // Position.
float radius;
float colR,colG,colB,colA; // Color rgba components.
} VType;
Байты положения, радиуса и цвета должны учитываться для расположения вершины, размера точки и цвета соответственно. Идентификаторы для них инициализируются:
ID_ATT_Pos = 0;
ID_ATT_Radius = 1;
ID_ATT_Color = 2;
// Note: I have also tried values of 1,2,3 but no difference.
Шаг для них задается в каждом вызове glVertexAttribPointer.
Предполагается,что каждая вершина будет нарисована в своей позиции x, y с заданным цветом и размером точки ее радиуса. С каждым вышеупомянутым атрибутом связана вершина атрибут шейдера, это "a_position", "a_color"и " a_radius". Вот шейдеры вершин и фрагментов:
VertexShader.txt
attribute vec2 a_position;
attribute vec4 a_color;
attribute float a_radius;
varying vec4 v_color;
void main()
{
gl_Position = vec4(a_position,0.0,1.0);
gl_PointSize = a_radius;
v_color = a_color;
}
Фрагментшейдер.txt
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
void main()
{
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
интересно, требуется ли проекционная матрица в вершинном шейдере? Все точки, которые я создаю, являются 2D в размерах экрана iPhone, и, как вы можете видеть, у каждой из них есть 'z, w', добавленные как '0.0, 1.0' в шейдере вершин выше.
Оставшийся основной код для настройки Ниже перечислены VBO и рендеринг с использованием glDrawElements. При выполнении этого кода очевидно, что glClear был успешным, и действительно печать NSLog подтверждает это, однако никакие вершины VType не рисуются в видовом окне кодом DrawFrame, перечисленным ниже. Координаты вершин хорошо вписываются в размеры экрана, например x, y: (92, 454).
Обратите внимание, что любые необъявленные переменные в следующем коде являются свойствами класса и соответствующего типа, например, " vao "- это GLuint, "vbos" - это GLuint[2], "программа" - это дескриптор программы GLuint. Я также опустил стандартный установочный код OpenGL, который был протестирован с различными внутренними кодами и показал, что он работает.
Загрузить Код Шейдера
-(GLuint)loadShaderType:(GLenum)type From:(NSString*)shaderFile {
GLuint shader;
GLint compiled;
// Create and compile vertex shader.
NSString *filepath = [[NSBundle mainBundle] pathForResource:shaderFile ofType:@"txt"];
const GLchar *shaderSrc = (GLchar *)[[NSString stringWithContentsOfFile:filepath encoding:NSUTF8StringEncoding error:nil] UTF8String];
if (!shaderSrc) {
NSLog(@"Failed to load vertex shader");
return 0;
}
// Create shader object.
shader = glCreateShader(type);
if (shader == 0) return 0;
// Load shader source.
glShaderSource(shader, 1, &shaderSrc, NULL);
// Compile shader.
glCompileShader(shader);
// Check compile status.
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char * infoLog = (char*)malloc(sizeof(char)*infoLen);
glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
NSLog(@"Error compiling shader:n%sn",infoLog);
free(infoLog);
}
glDeleteShader(shader);
return 0;
}
return shader;
}
Код Инициализации
GLfloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
GLfloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
glViewport(0, 0, screenWidth, screenHeight);
glGenVertexArraysOES(1, &vao);
glBindVertexArrayOES(vao);
// Generate buffer, bind to use now, set initial data.
glGenBuffers(2, vbos);
glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
glBufferData(GL_ARRAY_BUFFER, vxBufSize, squidVxs, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ixBufSize, squidIxs, GL_STATIC_DRAW);
glEnableVertexAttribArray(ID_ATT_Pos); // Pos
glVertexAttribPointer(ID_ATT_Pos, 2, GL_FLOAT, GL_FALSE, sizeof(VType), BUFFER_OFFSET(0));
glEnableVertexAttribArray(ID_ATT_Radius);// Radius
glVertexAttribPointer(ID_ATT_Radius, 1, GL_FLOAT, GL_FALSE, sizeof(VType), BUFFER_OFFSET(sizeof(float)*2));
glEnableVertexAttribArray(ID_ATT_Color);// Color
glVertexAttribPointer(ID_ATT_Color, 4, GL_FLOAT, GL_FALSE, sizeof(VType), BUFFER_OFFSET(sizeof(float)*3));
GLuint shaders[2];
shaders[0] = [self loadShaderType:GL_VERTEX_SHADER From:@"VertexShader"];
shaders[1] = [self loadShaderType:GL_FRAGMENT_SHADER From:@"FragmentShader"];
program = glCreateProgram();
glAttachShader(program, shaders[0]);
glAttachShader(program, shaders[1]);
glBindAttribLocation(program, ID_ATT_Pos, "a_position");
glBindAttribLocation(program, ID_ATT_Radius, "a_radius");
glBindAttribLocation(program, ID_ATT_Color, "a_color");
glLinkProgram(program);
GLint linked;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (!linked) {
GLint infoLen = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char* infoLog = (char*)malloc(sizeof(char)*infoLen);
glGetProgramInfoLog(program, infoLen, NULL, infoLog);
NSLog(@"Error linking program:n%sn",infoLog);
free(infoLog);
}
glDeleteProgram(program);
}
DrawFrame Code
// Note: Framebuffer swapping is taken care of before/after these
// lines, and has been shown to work.
glClearColor(0.33f, 0.0f, 0.33f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program);
glDrawElements(GL_POINTS, numPoints, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));
Дайте мне знать, если потребуется какая-либо другая информация, спасибо за ваше время.1 ответ:
Вам не обязательно нужна матрица проекции для ваших 2d вершин, но вам определенно нужно преобразовать все 3 координаты в диапазон [-1,1] (что вы уже сделали для z, установив его в 0). Эти координаты затем преобразуются GL с текущим преобразованием видового экрана (которое обычно должно соответствовать вашим размерам экрана). Поэтому, если координаты находятся в размерах экрана, то преобразуйте их в [-1,1] в шейдере или просто используйте координаты [-1,1] в приложении (что также было бы более решительным агностиком).
EDIT: и я не знаю, как OpenGL ES справляется с этим, но в desktop GL (по крайней мере до 2.1) вам нужно вызвать
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)
, чтобы вершинный шейдер смог вычислить размер точки.