Hallo Leute,
ich dachte mir, ich versuche mich mal ein wenig an OpenGL. Ich hab nix großes vor damit, sondern will lediglich ein paar Erfahrungen sammeln. Dafür will ich eine Klasse schreiben, die mir ein Fenster unter Windows initialisiert und darin etwas rendert. Anfangen wollte ich mit einem Dreieck und den üblichen Bunt gemischten Farben.
Nun ist es so, dass das Dreieck (bzw. zwei Dreiecke) gerendert wird, jedoch ist es nur weiß. Von Farbe keine Spur.
Kann mir vielleicht jemand Hinweise geben, wo ich etwas falsch gemacht habe?
Das Ganze ist ein Win32 Konsolenprojekt (!) in dem ich zur Laufzeit mit der Windows API ein Fenster erstelle. Dabei habe ich mich an einem Tutorial aus der OpenGL Wiki orientiert:
Link
Eine weitere Informationsquelle war:
Link
Hier die relevanten Codezeilen:
Die Klassendefinition von COpenGL
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class COpenGL
{
public:
COpenGL();
~COpenGL();
void showWindow();
bool createOpenGLContext();
void prepareScene();
void setData();
void setViewport(int w, int h);
void DrawScene();
void DestroyScene();
private:
std::vector<GLuint> vertexObject;
std::vector<GLuint> vertexBuffer;
GLuint program;
HWND hWnd;
MSG msg;
HDC hDC;
HGLRC hRC;
RECT rect;
protected:
static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
};
|
Die Methode der Klasse COpenGL, die das Fenster erstellt. Sie holt sich zuerst das Fenster Handle von dem Konsolenfenster und erzeugt dann mit CreateWindow() ein neues Fenster, in welches gerendert wird.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
void COpenGL::showWindow()
{
HWND hWndConsole = GetConsoleWindow();
HINSTANCE hInstance = (HINSTANCE)GetWindowLong(hWndConsole, GWL_HINSTANCE);
WNDCLASS wc;
wc.hbrBackground =(HBRUSH)GetStockObject(BLACK_BRUSH);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = StaticWndProc;
wc.lpszClassName = L"Game1";
wc.style = CS_OWNDC;
wc.lpszMenuName = NULL;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
if(!RegisterClass(&wc))
{
std::cout << "Die Fensterklasse konnte nicht registriert werden." << std::endl;
return;
}
//hWnd ist ein Attribut der Klasse vom Typ HWND und beinhaltet das Handle zu dem Fenster in welches gerendert wird.
hWnd = CreateWindow(L"Game1", L"Game 1", WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE, 520, 20, 300, 300,
NULL, NULL, hInstance, this);
hDC = GetDC(hWnd);
if(!hDC)
return;
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
//Hier wird der OpenGL Kontext erzeugt, siehe unten...
if(!createOpenGLContext())
{
std::cout << "Fehler beim Initialisieren von OpenGL" << std::endl;
return;
}
GetClientRect(hWnd, &rect);
setData();//Hier werden die Vertexbuffer initialisiert
prepareScene();//Vorbereitungen für die Scene
setViewport(rect.right, rect.bottom);
while(1)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) == TRUE)
{
if(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
return ;
}
}
//rendern...
DrawScene();
}
if (hRC)
wglDeleteContext(hRC);
if (hDC)
ReleaseDC(hWnd, hDC);
hRC = 0;
hDC = 0;
DestroyScene();
}
|
In dieser Methode erzeuge ich den OpenGL Kontext, damit in dem Fenster gerendert werden kann.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
bool COpenGL::createOpenGLContext()
{
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;//24, wenn der Stencil Buffer 8 Bits bekommt.
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
int pixelFormat = ChoosePixelFormat(hDC, &pfd);
if(!pixelFormat)
return false;
if(!SetPixelFormat(hDC, pixelFormat, &pfd))
return false;
//Hier wird ein temporärer RenderContext erzeugt. Zum initialisieren und als Fallback.
HGLRC tempRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, tempRC);
GLenum error = glewInit();
if(GLEW_OK != error)
{
MessageBox(NULL, L"Glew konnte nicht initialisiert werden.", L"Error", MB_OK);
}
int attributes[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
0};
if(wglewIsSupported("WGL_ARB_create_context") == 1)
{
hRC = wglCreateContextAttribsARB(hDC, 0, attributes);
wglMakeCurrent(0, 0);
wglDeleteContext(tempRC);
wglMakeCurrent(hDC, hRC);
}
else //Fallback: 3.1 wird nicht unterstützt -> Rückfall auf 2.1
{
hRC = tempRC;
}
int OpenGLVersion[2];
glGetIntegerv(GL_MAJOR_VERSION, &OpenGLVersion[0]);
glGetIntegerv(GL_MINOR_VERSION, &OpenGLVersion[1]);
if(!hRC)
return false;
std::cout << "OpenGL wurde erfolreich Initiatlisiert! Version: " << OpenGLVersion[0] << "." << OpenGLVersion[1] << std::endl;
return true;
}
|
Hier wird der VertexShader und der Fragment Shader geladen.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
void COpenGL::prepareScene()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
//Shader laden
std::string vertexShaderCode = "#version 140\n"
"in vec3 in_Position;\n"
"in vec3 in_Color;\n"
"out vec3 ex_Color;\n"
"void main(void)\n"
"{\n"
"gl_Position = vec4(in_Position, 1.0);\n"
"ex_Color = in_Color;\n}";
std::string fragmentShaderCode = "#version 140\n"
"precision highp float; // needed only for version 1.30\n"
"in vec3 ex_Color;\n"
"out vec4 out_Color;\n"
"void main(void)\n"
"{\n"
"out_Color = vec4(ex_Color,1.0);\n"
"}\n";
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
const char* v = vertexShaderCode.c_str();
GLint vlen = vertexShaderCode.length();
glShaderSource(vertexShader, 1, &v, &vlen);
const char* f = fragmentShaderCode.c_str();
GLint flen = fragmentShaderCode.length();
glShaderSource(fragmentShader, 1, &f, &flen);
glCompileShader(vertexShader);
GLint compiled;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{
std::cout << "Vertex Shader konnte nicht kompiliert werden!\n";
int infoLogLen = 0;
int charsWritten = 0;
GLchar *infoLog;
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &infoLogLen);
if (infoLogLen > 0)
{
infoLog = new GLchar[infoLogLen];
glGetShaderInfoLog(vertexShader,infoLogLen, &charsWritten, infoLog);
std::cout << "Infolog des Vertex Shaders:" << std::endl << infoLog << std::endl;
delete [] infoLog;
}
}
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{
std::cout << "Fragment Shader konnte nicht kompiliert werden!\n";
int infoLogLen = 0;
int charsWritten = 0;
GLchar *infoLog;
glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &infoLogLen);
if (infoLogLen > 0)
{
infoLog = new GLchar[infoLogLen];
glGetShaderInfoLog(fragmentShader, infoLogLen, &charsWritten, infoLog);
std::cout << "Infolog des Fragment Shaders:" << std::endl << infoLog << std::endl;
delete [] infoLog;
}
}
program = glCreateProgram();
glBindAttribLocation(program, 0, "in_Position");
glBindAttribLocation(program, 0, "in_Color");
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
glUseProgram(program);
}
|
In setData werden die Vertexbuffer initialisiert.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
void COpenGL::setData()
{
// First simple object
std::vector<float> vert(9);
std::vector<float> col(9);// color array
//x, y, z
//x: -1 -> 1
//y: 1 -> -1
vert[0] = .5f; vert[1] = .5f; vert[2] = 0.f;//1. Punkt
vert[3] = 0.f; vert[4] = -.5f; vert[5] = 0.f;//2. Punkt
vert[6] = -.5f; vert[7] = .5f; vert[8]= 0.f;//3. Punkt
col[0] = 1.f; col[1] = 0.0f; col[2] = 0.0f;
col[3] = 0.0f; col[4] = 1.0f; col[5] = 0.0f;
col[6] = 0.0f; col[7] = 0.0f; col[8] = 1.0f;
// Second simple object
std::vector<float> vert2(9);// vertex array
vert2[0] = 0.5f; vert2[1] = -.5f; vert2[2] = 0.f;//1. Punkt
vert2[3] = 0.f; vert2[4] = -.8f; vert2[5] = 0.f;//2. Punkt
vert2[6] = -.5f; vert2[7] = -.5f; vert2[8]= 0.f;//3. Punkt
// Two VAOs allocation
//vertexArray
vertexObject.resize(2);
glGenVertexArrays(2, &vertexObject[0]);
// First VAO setup
glBindVertexArray(vertexObject[0]);
vertexBuffer.resize(3);
glGenBuffers(2, &vertexBuffer[0]);
//Let's go
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
glBufferData(GL_ARRAY_BUFFER, 9*sizeof(GLfloat), &vert[0], GL_STATIC_DRAW);
glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
//Color
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]);
glBufferData(GL_ARRAY_BUFFER, 9*sizeof(GLfloat), &col[0], GL_STATIC_DRAW);
glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
// Second VAO setup
glBindVertexArray(vertexObject[1]);
glGenBuffers(1, &vertexBuffer[2]);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]);
glBufferData(GL_ARRAY_BUFFER, 9*sizeof(GLfloat), &vert2[0], GL_STATIC_DRAW);
glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
}
|
Und zum Schluss die Methode in der der Rendervorgang stattfindent
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
void COpenGL::DrawScene()
{
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vertexObject[0]);// select first VAO
glDrawArrays(GL_TRIANGLES, 0, 3);// draw first object
glBindVertexArray(vertexObject[1]);// select second VAO
glVertexAttrib3f((GLuint)1, 1.0, 0.0, 0.0); // set constant color attribute
glDrawArrays(GL_TRIANGLES, 0, 3);// draw second object
glBindVertexArray(0);
SwapBuffers(hDC);
}
|
Das Design des Klassenaufbaus ist noch nicht ganz final, es ist eben noch WIP.
Ich hoffe, dass es nicht zu viel Code ist und verständlich ist. Wenn ihr noch Fragen habt, immer her damit.