OpenGL:创建一个彩色三角形

时间:2015-10-31 16:39:26

标签: opengl

我最近在学习OpenGL,我想在每个点画一个红色,绿色,蓝色的三角形。但是,当我使用以下代码时,它只显示一个空白的白色窗口。我找不到错误在哪里。我遵循这个web site和红皮书。

#include <GL/glew.h>
#include <GL/freeglut.h>
#include <iostream>

using namespace std;

const int WIDTH = 300;
const int HEIGHT = 300;

enum VAO_IDs {Triangles,NumVAOs};
enum Buffer_IDs {ArrayBuffer,NumBuffers};
enum Attrib_IDs{vVertex,vColor};

GLuint VAOs[NumVAOs];
GLuint Buffers[NumBuffers];
static const GLchar* vertexSource[] = {
    "#version 330\n"
    "in vec3 position;\n"
    "in vec3 color;\n"

    "out vec3 Color;\n"

    "void main()\n"
    "{\n"
    "   Color = color;\n"
    "   gl_Position = vec4(position, 1.0);\n"
    "}\n" };

static const GLchar* fragmentSource[] = {
    "#version 330\n"

    "in vec3 Color;\n"

    "out vec4 outColor;\n"

    "void main()\n"
    "{"
    "   outColor = vec4(Color, 1.0);\n"
    "}" };





void OnInit() {

    //Generate vertex objects 
    //Bind vertex objects
    glGenVertexArrays(NumVAOs, VAOs);
    glBindVertexArray(VAOs[Triangles]);

    //Input vertices information
    GLfloat vertices[] = {
         0.0f,  0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // Vertex 1: Red
         0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,// Vertex 2: Green
        -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f// Vertex 3: Blue
    };

    //Generate Buffer object
    //Bind Buffer object
    //Bind Buffer data
    glGenBuffers(NumBuffers,Buffers);
    glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    //Generate vertexshader
    //Bind vertexshader source
    //Compile vertexshader
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, vertexSource, NULL);
    glCompileShader(vertexShader);
    //debug
    GLint vertexShader_status;
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &vertexShader_status);
    if (!vertexShader_status){
        GLsizei log_length = 0;
        GLchar message[1024];
        glGetShaderInfoLog(vertexShader, 1024, &log_length, message);
        std::cout << message << std::endl;
    }

    //Generate fragmentshader
    //Bind fragmentshader source
    //Compile fragmentshader
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, fragmentSource, NULL);
    GLint fragmentShader_status;
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &fragmentShader_status);
    if (!fragmentShader_status){
        GLsizei log_length = 0;
        GLchar message[1024];
        glGetShaderInfoLog(vertexShader, 1024, &log_length, message);
        std::cout << message << std::endl;
    }

    //Generate program
    //Attach attributes to the program
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    glUseProgram(shaderProgram);

    //Figure out position attributes information
    GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
    cout << "vPosition: " << posAttrib << endl;
    glEnableVertexAttribArray(posAttrib);
    glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE,
        6 * sizeof(GLfloat), 0);

    //Figure out color attributes information
    GLint colAttrib = glGetAttribLocation(shaderProgram, "color");
    cout << "vColor: " << colAttrib << endl;
    glEnableVertexAttribArray(colAttrib);
    glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE,
        6 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));

    cout << "Initialization successfull" << endl;
}
void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBindVertexArray(VAOs[Triangles]);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glFlush();
    cout << "Display successfull" << endl;

}
void OnShutdown() {
    cout << "Shutdown successfull" << endl;
}
void OnResize(int nw, int nh) {
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE |
        GLUT_RGBA);
    glutInitContextVersion(3, 3);
    glutInitContextFlags(GLUT_CORE_PROFILE | GLUT_DEBUG);
    glutInitContextProfile(GLUT_FORWARD_COMPATIBLE);
    glutInitWindowSize(WIDTH, HEIGHT);
    glutInitContextFlags(GLUT_CORE_PROFILE | GLUT_DEBUG);
    glutInitContextProfile(GLUT_FORWARD_COMPATIBLE);
    glutInitWindowSize(WIDTH, HEIGHT);
    glutCreateWindow("Getting started with OpenGL 3.3");
    glewExperimental = GL_TRUE;
    GLenum err = glewInit();
    if (GLEW_OK != err){
        cerr << "Error: " << glewGetErrorString(err) << endl;
    }
    else {
        if (GLEW_VERSION_3_3)
        {
            cout << "Driver supports OpenGL 3.3\nDetails:" << endl;
        }
    }
    cout << "\tUsing glew " << glewGetString(GLEW_VERSION) << endl;
    cout << "\tVendor: " << glGetString(GL_VENDOR) << endl;
    cout << "\tRenderer: " << glGetString(GL_RENDERER) << endl;
    cout << "\tVersion: " << glGetString(GL_VERSION) << endl;
    cout << "\tGLSL:"<<glGetString(GL_SHADING_LANGUAGE_VERSION)<<endl;
    OnInit();
    glutDisplayFunc(display);
    glutReshapeFunc(OnResize);
    glutMainLoop();
    return 0;
}

2 个答案:

答案 0 :(得分:3)

有两个小而重要的问题会阻止您的代码工作:

  • 您永远不会编译片段着色器。你有这个代码:

    glShaderSource(fragmentShader, 1, fragmentSource, NULL);
    GLint fragmentShader_status;
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &fragmentShader_status);
    

    请注意,glCompileShader()来电缺失。并且您正在检查顶点着色器的编译状态,因此您的错误检查不会触发。将其更改为:

    glShaderSource(fragmentShader, 1, fragmentSource, NULL);
    glCompileShader(fragmentShader);
    GLint fragmentShader_status;
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &fragmentShader_status);
    
  • 由于您使用双缓冲,由此处传递的GLUT_DOUBLE标志给出:

    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    

    您需要在显示功能结束时调用glutSwapBuffers()而不是glFlush()。将display()函数的最后部分更改为:

    glutSwapBuffers();
    cout << "Display successfull" << endl;
    

通过这些更改,您的代码适合我。

答案 1 :(得分:0)

您可以使用此代码: 我现在不会深入研究细节,但您需要创建一个顶点数组对象并将其设置为当前对象     GLuint VertexArrayID;     glGenVertexArrays(1,&amp; VertexArrayID);     glBindVertexArray(VertexArrayID); 在创建窗口后(=在创建OpenGL上下文之后)和任何其他OpenGL调用之前执行此操作。

三角形由三个点定义。在谈论3D图形中的“点”时,我们通常使用单词“vertex”(复数上的“顶点”)。顶点有3个坐标:X,Y和Z.您可以通过以下方式考虑这三个坐标:

X in on your right
Y is up
Z is towards your back (yes, behind, not in front of you)

但是这里有一个更好的可视化方法:使用右手规则

X is your thumb
Y is your index
Z is your middle finger. If you put your thumb to the right and your index to the sky, it will point to your back, too.

Z朝这个方向很奇怪,为什么会这样呢?简短的回答:因为100年的右手规则数学将为您提供许多有用的工具。唯一的缺点是不直观的Z。

在旁注中,请注意您可以自由地移动您的手:您的X,Y和Z也将移动。稍后会详细介绍。

所以我们需要三个3D点来制作一个三角形;我们走吧 :     //一个包含3个顶点的3个向量的数组     static const GLfloat g_vertex_buffer_data [] = {     -1.0f,-1.0f,0.0f,     1.0f,-1.0f,0.0f,     0.0f,1.0f,0.0f, }; 第一个顶点是(-1,-1,0)。这意味着除非我们以某种方式对其进行转换,否则它将在屏幕上的(-1,-1)处显示。这是什么意思 ?屏幕原点位于中间,X位于右侧,正常情况下,Y向上。这是它在宽屏幕上提供的内容: 下一步是将此三角形赋予OpenGL。我们通过创建缓冲区来实现这一点:

// This will identify our vertex buffer
GLuint vertexbuffer;

// Generate 1 buffer, put the resulting identifier in vertexbuffer
glGenBuffers(1, &vertexbuffer);

// The following commands will talk about our 'vertexbuffer' buffer
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);

// Give our vertices to OpenGL.
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data),
g_vertex_buffer_data, GL_STATIC_DRAW);

这只需要进行一次。

现在,在我们用于绘制“无”的主循环中,我们可以画出我们宏伟的三角形:     //第一个属性缓冲区:顶点     glEnableVertexAttribArray(0);     glBindBuffer(GL_ARRAY_BUFFER,vertexbuffer);     glVertexAttribPointer(     0,//属性0.没有特别的原因为0,但必须匹配着色器中的布局。     3,//大小     GL_FLOAT,//类型     GL_FALSE,//规范化了吗?     0,//大步     (void *)0 //数组缓冲区偏移量     );

// Draw the triangle !
glDrawArrays(GL_TRIANGLES, 0, 3); // Starting from vertex 0; 3 vertices 

total -> 1 triangle

glDisableVertexAttribArray(0);

着色器编译

在最简单的配置中,您将需要两个着色器:一个名为Vertex Shader,将为每个顶点执行,另一个称为Fragment Shader,将为每个样本执行。由于我们使用4x抗锯齿,因此每个像素中有4个样本。

着色器使用一种名为GLSL的语言进行编程:GL着色语言,它是OpenGL的一部分。与C或Java不同,GLSL必须在运行时进行编译,这意味着每次启动应用程序时,都会重新编译所有着色器。

这两个着色器通常位于不同的文件中。在这个例子中,我们有SimpleFragmentShader.fragmentshader和SimpleVertexShader.vertexshader。扩展名无关紧要,可能是.txt或.glsl。

所以这是代码。完全理解它并不是很重要,因为你经常只在程序中这样做一次,所以评论应该足够了。由于此函数将被所有其他教程使用,因此它将放在一个单独的文件中:common / loadShader.cpp。请注意,就像缓冲区一样,着色器无法直接访问:我们只有一个ID。实际的实现隐藏在驱动程序中。 GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){

// Create the shaders
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

// Read the Vertex Shader code from the file
std::string VertexShaderCode;
std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
if(VertexShaderStream.is_open())
{
    std::string Line = "";
    while(getline(VertexShaderStream, Line))
        VertexShaderCode += "\n" + Line;
    VertexShaderStream.close();
}

// Read the Fragment Shader code from the file
std::string FragmentShaderCode;
std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
if(FragmentShaderStream.is_open()){
    std::string Line = "";
    while(getline(FragmentShaderStream, Line))
        FragmentShaderCode += "\n" + Line;
    FragmentShaderStream.close();
}

GLint Result = GL_FALSE;
int InfoLogLength;

// Compile Vertex Shader
printf("Compiling shader : %s\n", vertex_file_path);
char const * VertexSourcePointer = VertexShaderCode.c_str();
glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
glCompileShader(VertexShaderID);

// Check Vertex Shader
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
std::vector<char> VertexShaderErrorMessage(InfoLogLength);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);

// Compile Fragment Shader
printf("Compiling shader : %s\n", fragment_file_path);
char const * FragmentSourcePointer = FragmentShaderCode.c_str();
glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
glCompileShader(FragmentShaderID);

// Check Fragment Shader
glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
std::vector<char> FragmentShaderErrorMessage(InfoLogLength);
glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]);

// Link the program
fprintf(stdout, "Linking program\n");
GLuint ProgramID = glCreateProgram();
glAttachShader(ProgramID, VertexShaderID);
glAttachShader(ProgramID, FragmentShaderID);
glLinkProgram(ProgramID);

// Check the program
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
std::vector<char> ProgramErrorMessage( max(InfoLogLength, int(1)) );
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);

glDeleteShader(VertexShaderID);
glDeleteShader(FragmentShaderID);

return ProgramID;

} 让我们先写一下顶点着色器。 第一行告诉编译器我们将使用OpenGL 3的语法。     #version 330核心

第二行声明输入数据:     vec3中的layout(location = 0)vertexPosition_modelspace; 让我们详细解释这一行:

“vec3″ is a vector of 3 components in GLSL. It is similar (but different) to the glm::vec3 we used to declare our triangle. The important thing is that if we use 3 components in C++, we use 3 components in GLSL too.
“layout(location = 0)” refers to the buffer we use to feed the vertexPosition_modelspace attribute. Each vertex can have numerous attributes : A position, one or several colours, one or several texture coordinates, lots of other things. OpenGL doesn’t know what a colour is : it just sees a vec3. So we have to tell him which buffer corresponds to which input. We do that by setting the layout to the same value as the first parameter to glVertexAttribPointer. The value “0″ is not important, it could be 12 (but no more than glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &v) ), the important thing is that it’s the same number on both sides.
“vertexPosition_modelspace” could be anything else. It will contain the position of the vertex for each run of the vertex shader.
“in” means that this is some input data. Soon we’ll see the “out” keyword.

为每个顶点调用的函数称为main,就像在C中一样:     void main(){ 我们的主要功能只是将顶点的位置设置为缓冲区中的任何位置。因此,如果我们给(1,1),三角形的顶点将位于屏幕的右上角。我们将在下一个教程中看到如何对输入位置进行一些更有趣的计算。      gl_Position.xyz = vertexPosition_modelspace;      gl_Position.w = 1.0;      } gl_Position是为数不多的内置变量之一:您必须为其分配一些值。其他一切都是可选的。

对于我们的第一个片段着色器,我们将做一些非常简单的事情:将每个片段的颜色设置为红色。 (请记住,像素中有4个片段,因为我们使用4x AA)     #version 330核心     vec3颜色;

void main(){
    color = vec3(1,0,0);
}

所以是的,vec3(1,0,0)意味着红色。这是因为在计算机屏幕上,颜色按此顺序由红色,绿色和蓝色三联体表示。所以(1,0,0)表示全红,没有绿色,没有蓝色。 在主循环之前,调用我们的LoadShaders函数:     //从着色器创建并编译我们的GLSL程序     GLuint programID = LoadShaders(&#34; SimpleVertexShader.vertexshader&#34;,
    &#34; SimpleFragmentShader.fragmentshader&#34; ); 现在在主循环内,首先清除屏幕。由于主循环上方的glClearColor(0.0f,0.0f,0.4f,0.0f)调用,这会将背景颜色更改为深蓝色:     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 然后告诉OpenGL您要使用着色器:
   //使用我们的着色器    glUseProgram(programID);

//绘制三角形......