본문 바로가기

Game/Graphics

OpenGL-Tutorial 8 : Basic shading

link : http://www.opengl-tutorial.org/kr/beginners-tutorials/tutorial-8-basic-shading/


8번째 튜토리얼이다. 이번 튜토리얼에서는 기본적인 쉐이딩을 수행하는 방법을 학습한다고 한다.


1) 광원에 가까울 때 더 밝은 빛

2) 빛의 반사를 바라볼 때 하이라트가 있다(반사 조명)

3) 빛이 모델쪽으로 직접 향하지 않을 때 어두워 진다(확산 조명)



Triangle normalse (삼각형 법선)


평면의 법선은 이 평면에 수직인 길이 1의 벡터이다.

삼각형의 법선은 이 삼각형에 수직인 길이 1의 벡터이다. 그것은 두 개의 모서리의 외적을 취함으로써 쉽게 계산된다.

triangle ( v1, v2, v3 )
edge1 = v2-v1
edge2 = v3-v1
triangle.normal = cross(edge1, edge2).normalize()

normal과 normalize()를 섞지 말아라. Normalize()는 새 길이가 1이 되도록 벡터를 길이로 나눈다.

normal은 나타낼 수 있는 일부 벡터의 이름으로 보통은 법선이다.



Vertex normals (정점 법선)


확장에 의해, 정점의 법선을 주위 삼각형의 법선의 조합이라고 부른다. Vertex Shader에서는 삼각형이 아닌 vertex를 다루기 때문에 편리하고, 그래서 vertex에 대한 정보를 가지고 있는 것이 좋다.

vertex v1, v2, v3, ....
triangle tr1, tr2, tr3 // all share vertex v1
v1.normal = normalize( tr1.normal + tr2.normal + tr3.normal )


Using vertex normals in OpenGL


OpenGL에서 법선을 사용하는 것은 매우 쉽다. 법선은 위치, 색상, UV 좌표와 같은 꼭지점의 속성이다. 일반적인 물건만 수행하면 된다.

GLuint normalbuffer;
 glGenBuffers(1, &normalbuffer);
 glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
 glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals[0], GL_STATIC_DRAW);

and

 // 3rd attribute buffer : normals
 glEnableVertexAttribArray(2);
 glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
 glVertexAttribPointer(
     2,                                // attribute
     3,                                // size
     GL_FLOAT,                         // type
     GL_FALSE,                         // normalized?
     0,                                // stride
     (void*)0                          // array buffer offset
 );


The Diffuse part


The importance of the surface normal


빛이 물체에 부딪히면, 그 중 중요한 부분이 모든 방향으로 반사된다. 이것은 "diffuse component"이다.



어떤 광속이 표면에 도달하면 이 표면은 빛이 도달하는 각도에 따라 다르게 조명된다.


빛이 표면에 수직인 경우에는 작은 표면에 집중된다. 응시 각도에 도달하면 같은 양의 빛이 더 넓은 표면에 확산된다.




이것은 표면의 각 점이 응시 빛으로 어둡게 보일 것임을 의미한다. (그러나 더 많은 점들이 조명 될 것이므로 총 광량은 동일하게 유지됨)


즉, 픽셀의 색상을 계산할 때 들어오는 빛과 표면 간의 각도가 중요하다.

// Cosine of the angle between the normal and the light direction,
// clamped above 0
//  - light is at the vertical of the triangle -> 1
//  - light is perpendicular to the triangle -> 0
float cosTheta = dot( n,l );

color = LightColor * cosTheta;

이 코드에서 n은 표면 법선이고. l은 표면에서 빛으로 이동하는 단위 벡터이다.



Beware of the sign


우리 cosTheta의 공식에 무엇ㅇ신가가 빠져있다. 빛이 삼각형 뒤에 있으면 n과 l이 반대이므로 n.l은 음수가 된다.


이것은 color = someNegativeNumber를 의미할 것이며 이는 별 의미가 없다. 그래서 우리는 cosTheta를 0으로 clamp해야한다.

// Cosine of the angle between the normal and the light direction,
// clamped above 0
//  - light is at the vertical of the triangle -> 1
//  - light is perpendicular to the triangle -> 0
//  - light is behind the triangle -> 0
float cosTheta = clamp( dot( n,l ), 0,1 );

color = LightColor * cosTheta;


Material Color


물론 출력 색상도 재질의 색상에 따라 다르다. 이 이미지에서 흰색 빛은 녹색, 빨간색 및 파란색 빛으로 만들어진다.


적색 물질과 충돌하면 녹색과 청색 빛이 흡수되고, 적색 물질만 남는다.



간단한 곱셈으로 이것을 모델링 할 수 있다.

color = MaterialDiffuseColor * LightColor * cosTheta;



Modeling the light


우리는 먼저 우리가 촛불과 같이 공간의 모든 방향으로 방출하는 빛을 가지고 있다고 가정한다.


그러한 빛과 함께, 우리의 표면이 받을 광속은 광원까지의 거리에 따라 달라진다. 멀리 떨어져있을수록 빛은 적다. (빛의 양은 거리의 제곱으로 줄어든다)

color = MaterialDiffuseColor * LightColor * cosTheta / (distance*distance);

마지막으로 우리는 빛의 힘을 제어하는 또 다른 매개 변수가 필요하다. 이것은 LightColor로 인코딩 될 수 있다. 하지만 이제 color와 power만 있으면 된다.

color = MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance);




Putting it all together


이 코드가 작동하려면 소수의 매개 변수와 더 많은 코드가 필요하다.


MaterialDiffuseColor는 텍스처에서 간단히 가져온다. LightColor와 LightPower는 GLSL 유니폼을 통해 셰이더에 설정된다.


cosθ는 n과 l에 의존한다. 우리는 어느 공간에서나 표현할 수 있다. 이 공간에서 빛의 위치를 계산하기 쉽기 때문에 카메라 공간을 선택한다.

// Normal of the computed fragment, in camera space
 vec3 n = normalize( Normal_cameraspace );
 // Direction of the light (from the fragment to the light)
 vec3 l = normalize( LightDirection_cameraspace );

Vertex 쉐이더에서 계산된 Normal_cameraspace 및 LightDirection_cameraspace를 사용해 프래그먼트 쉐이더로 전달

// Output position of the vertex, in clip space : MVP * position
gl_Position =  MVP * vec4(vertexPosition_modelspace,1);

// Position of the vertex, in worldspace : M * position
Position_worldspace = (M * vec4(vertexPosition_modelspace,1)).xyz;

// Vector that goes from the vertex to the camera, in camera space.
// In camera space, the camera is at the origin (0,0,0).
vec3 vertexPosition_cameraspace = ( V * M * vec4(vertexPosition_modelspace,1)).xyz;
EyeDirection_cameraspace = vec3(0,0,0) - vertexPosition_cameraspace;

// Vector that goes from the vertex to the light, in camera space. M is ommited because it's identity.
vec3 LightPosition_cameraspace = ( V * vec4(LightPosition_worldspace,1)).xyz;
LightDirection_cameraspace = LightPosition_cameraspace + EyeDirection_cameraspace;

// Normal of the the vertex, in camera space
Normal_cameraspace = ( V * M * vec4(vertexNormal_modelspace,0)).xyz; // Only correct if ModelMatrix does not scale the model ! Use its inverse transpose if not.

이 코드는 인상적으로 보일 수 있지만 튜토리얼 3에서 배운 것은 아니다. 각 벡터의 이름에 공간의 이름을 쓰려고 주의를 기울여 일어나는 일을 추적하는 것이 훨씬 쉽다.


M 및 V는 Model 및 View 행렬이며 MVP와 완전히 동일한 방식으로 셰이더에 전달된다.




**이 튜토리얼부터는 shader 코드가 길어지고 중요하므로 함께 업로드하도록 하겠다.


1) StandardShading.fragmentshader

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
#version 330 core
 
in vec2 UV;
in vec3 Position_worldspace;
in vec3 Normal_cameraspace;
in vec3 EyeDirection_cameraspace;
in vec3 LightDirection_cameraspace;
 
out vec3 color;
 
uniform sampler2D myTextureSampler;
uniform mat4 MV;
uniform vec3 LightPosition_worldspace;
 
void main(){
 
    vec3 LightColor = vec3(1,1,1);
    float LightPower = 50.0f;
 
    vec3 MaterialDiffuseColor = texture(myTextureSampler, UV ).rgb;
    vec3 MaterialAmbientColor = vec3(0.1,0.1,0.1) * MaterialDiffuseColor;
    vec3 MaterialSpecularColor = vec3(0.3,0.3,0.3);
 
    float distance = length( LightPosition_worldspace - Position_worldspace );
 
    vec3 n = normalize( Normal_cameraspace );
    vec3 l = normalize( LightDirection_cameraspace );
 
    float cosTheta = clamp( dot( n,l), 0,1 );
 
    vec3 E = normalize(EyeDirection_cameraspace);
    vec3 R = reflect(-l,n);
 
    float cosAlpha = clamp( dot( E,R ), 0,1 );
 
    color = MaterialAmbientColor + 
    MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) +
    MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5) / (distance*distance);
}
cs


2) StandardShading.vertexshader

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
#version 330 core
 
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
layout(location = 2) in vec3 vertexNormal_modelspace;
 
out vec2 UV;
out vec3 Position_worldspace;
out vec3 Normal_cameraspace;
out vec3 EyeDirection_cameraspace;
out vec3 LightDirection_cameraspace;
 
uniform mat4 MVP;
uniform mat4 V;
uniform mat4 M;
uniform vec3 LightPosition_worldspace;
 
void main(){
 
    gl_Position = MVP * vec4(vertexPosition_modelspace,1);
 
    Position_worldspace = (M * vec4(vertexPosition_modelspace,1)).xyz;
 
    vec3 vertexPosition_cameraspace = ( V * M * vec4(vertexPosition_modelspace,1)).xyz;
    EyeDirection_cameraspace = vec3(0,0,0) - vertexPosition_cameraspace;
 
    vec3 LightPosition_cameraspace = (V * vec4(LightPosition_worldspace,1)).xyz;
    LightDirection_cameraspace = LightPosition_cameraspace + EyeDirection_cameraspace;
 
    Normal_cameraspace = ( V * M * vec4(vertexNormal_modelspace,0)).xyz;
 
    UV = vertexUV;
}
cs


3) source.cpp

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <GL/glew.h>
#include <glfw3.h>
GLFWwindow* window;
 
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
using namespace glm;
 
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
 
GLuint LoadShaders(const char *const char *);
GLuint loadBMP_custom(const char *);
GLuint loadDDS(const char *);
bool loadOBJ(
    const char *,
    std::vector<glm::vec3> &,
    std::vector<glm::vec2> &,
    std::vector<glm::vec3> &);
 
//mouse-keyboard input
void computeMatricesFromInputs();
glm::mat4 getViewMatrix();
glm::mat4 getProjectionMatrix();
 
glm::mat4 ViewMatrix;
glm::mat4 ProjectionMatrix;
 
glm::mat4 getViewMatrix() {
    return ViewMatrix;
}
glm::mat4 getProjectionMatrix() {
    return ProjectionMatrix;
}
 
//포지션 초기화
glm::vec3 position = glm::vec3(005);
float horizontalAngle = 3.14f;
float verticalAngle = 0.0f;
float initialFoV = 45.0f;
 
float speed = 3.0f;
float mouseSpeed = 0.005f;
 
int main() {
 
    // Initialise GLFW
    if (!glfwInit())
    {
        fprintf(stderr, "Failed to initialize GLFW\n");
        getchar();
        return -1;
    }
 
    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 
    // Open a window and create its OpenGL context
    window = glfwCreateWindow(1024768"QBOT_opengl"NULLNULL);
    if (window == NULL) {
        fprintf(stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
        getchar();
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
 
    // Initialize GLEW
    glewExperimental = true;
    if (glewInit() != GLEW_OK) {
        fprintf(stderr, "Failed to initialize GLEW\n");
        getchar();
        glfwTerminate();
        return -1;
    }
 
    // Ensure we can capture the escape key being pressed below
    glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
 
    // Set the mouse at the center of the screen
    glfwPollEvents();
    glfwSetCursorPos(window, 1024 / 2768 / 2);
 
    // Dark blue background
    glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
 
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    glEnable(GL_CULL_FACE);
 
    GLuint VertexArrayID;
    glGenVertexArrays(1&VertexArrayID);
    glBindVertexArray(VertexArrayID);
 
    //Shader를 불러온다.
    GLuint programID = LoadShaders("StandardShading.vertexshader""StandardShading.fragmentshader");
 
    //매트릭스ID 추가
    GLuint MatrixID = glGetUniformLocation(programID, "MVP");
    GLuint ViewMatrixID = glGetUniformLocation(programID, "V");
    GLuint ModelMatrixID = glGetUniformLocation(programID, "M");
 
    //어떠한 두 가지의 함수를 사용해서 텍스처를 불러온다
    //GLuint Texture = loadBMP_custom("uvtemplate.bmp");
    GLuint Texture = loadDDS("uvmap.DDS");
 
    GLuint TextureID = glGetUniformLocation(programID, "myTextureSampler");
 
    //우리의 .obj file을 읽는다
    std::vector<glm::vec3> vertices;
    std::vector<glm::vec2> uvs;
    std::vector<glm::vec3> normals;
    //bool res = loadOBJ("cube.obj", vertices, uvs, normals);
    bool res = loadOBJ("suzanne.obj", vertices, uvs, normals);
 
    GLuint vertexbuffer;
    glGenBuffers(1&vertexbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
 
    GLuint uvbuffer;
    glGenBuffers(1&uvbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
    glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(glm::vec2), &uvs[0], GL_STATIC_DRAW);
 
    GLuint normalbuffer;
    glGenBuffers(1&normalbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
    glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals[0], GL_STATIC_DRAW);
 
    glUseProgram(programID);
    GLuint LightID = glGetUniformLocation(programID, "LightPosition_worldspace");
    
 
    do {
        // Clear the screen. It's not mentioned before Tutorial 02, but it can cause flickering, so it's there nonetheless.
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
        glUseProgram(programID);
 
        //키보드와 마우스 인풋으로부터의 MVP 매트릭스를 계산한다
        computeMatricesFromInputs();
        glm::mat4 ProjectionMatrix = getProjectionMatrix();
        glm::mat4 ViewMatrix = getViewMatrix();
        glm::mat4 ModelMatrix = glm::mat4(1.0);
        glm::mat4 MVP = ProjectionMatrix*ViewMatrix*ModelMatrix;
 
        //transformation을 현재 쉐이더에 보냄
        glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
        glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, &ModelMatrix[0][0]);
        glUniformMatrix4fv(ViewMatrixID, 1, GL_FALSE, &ViewMatrix[0][0]);
 
        glm::vec3 lightPos = glm::vec3(444);
        glUniform3f(LightID, lightPos.x, lightPos.y, lightPos.z);
 
        //텍스처 유닛0에 있는 텍스처를 바인딩한다.
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, Texture);
 
        //1st 속성 버퍼 : vertices
        glUniform1i(TextureID, 0);
 
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
        glVertexAttribPointer(
            0,            //0번째 속성. 0이 될 특별한 이유는 없지만 쉐이더의 레이아웃과 반드시 맞춰야함
            3,            //크기(size)
            GL_FLOAT,    //타입(type)
            GL_FALSE,    //정규화(normalized)?
            0,            //다음 요소까지의 간격(stride)
            (void*)0    //배열 버퍼의 오프셋(offset)
        );
 
        //2nd 속성 버퍼 : UVs
        glEnableVertexAttribArray(1);
        glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
        glVertexAttribPointer(
            1,
            2,
            GL_FLOAT,
            GL_FALSE,
            0,
            (void*)0
        );
 
        //3rd 속성 버퍼 : normals
        glEnableVertexAttribArray(2);
        glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
        glVertexAttribPointer(
            2,            
            3,            
            GL_FLOAT,
            GL_FALSE,
            0,
            (void*)0
        );
 
        glDrawArrays(GL_TRIANGLES, 0, vertices.size());
 
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(2);
 
        // Swap buffers
        glfwSwapBuffers(window);
        glfwPollEvents();
 
    } // Check if the ESC key was pressed or the window was closed
    while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
        glfwWindowShouldClose(window) == 0);
 
    // Cleanup VBO
    glDeleteBuffers(1&vertexbuffer);
    glDeleteBuffers(1&uvbuffer);
    glDeleteBuffers(1&normalbuffer);
    glDeleteProgram(programID);
    glDeleteTextures(1&TextureID);
    glDeleteVertexArrays(1&VertexArrayID);
 
    // Close OpenGL window and terminate GLFW
    glfwTerminate();
 
    return 0;
}
 
GLuint LoadShaders(const char * vertex_file_path, const char * fragment_file_path) {
 
    //쉐이더 생성
    GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
    GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
 
    //버텍스 쉐이더 코드를 파일에서 읽기
    std::string VertexShaderCode;
    std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
    if (VertexShaderStream.is_open()) {
        std::stringstream sstr;
        sstr << VertexShaderStream.rdbuf();
        VertexShaderCode = sstr.str();
        VertexShaderStream.close();
    }
    else {
        printf("파일 %s를 읽을 수 없음. 정확한 디렉토리를 사용 중입니까?\n", vertex_file_path);
        getchar();
        return 0;
    }
 
    //프래그먼트 쉐이더 코드를 파일에서 읽기
    std::string FragmentShaderCode;
    std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
    if (FragmentShaderStream.is_open()) {
        std::stringstream sstr;
        sstr << FragmentShaderStream.rdbuf();
        FragmentShaderCode = sstr.str();
        FragmentShaderStream.close();
    }
 
    GLint Result = GL_FALSE;
    int InfoLogLength;
 
    //버텍스 쉐이더를 컴파일
    printf("Compiling shader : %s\n", vertex_file_path);
    char const * VertexSourcePointer = VertexShaderCode.c_str();
    glShaderSource(VertexShaderID, 1&VertexSourcePointer, NULL);
    glCompileShader(VertexShaderID);
 
    //버텍스 쉐이더를 검사
    glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
    glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    if (InfoLogLength > 0) {
        std::vector<char> VertexShaderErrorMessage(InfoLogLength + 1);
        glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL&VertexShaderErrorMessage[0]);
        printf("%s\n"&VertexShaderErrorMessage[0]);
    }
 
    //프래그먼트 쉐이더를 컴파일
    printf("Compiling shader : %s", fragment_file_path);
    char const * FragmentSourcePointer = FragmentShaderCode.c_str();
    glShaderSource(FragmentShaderID, 1&FragmentSourcePointer, NULL);
    glCompileShader(FragmentShaderID);
 
    //프래그먼트 쉐이더를 검사
    glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
    glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    if (InfoLogLength > 0) {
        std::vector<char> FragmentShaderErrorMessage(InfoLogLength + 1);
        glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL&FragmentShaderErrorMessage[0]);
        printf("%s\n"&FragmentShaderErrorMessage[0]);
    }
 
    //프로그램에 링크
    printf("Linking program\n");
    GLuint ProgramID = glCreateProgram();
    glAttachShader(ProgramID, VertexShaderID);
    glAttachShader(ProgramID, FragmentShaderID);
    glLinkProgram(ProgramID);
 
    //프로그램 검사
    glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
    glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    if (InfoLogLength > 0) {
        std::vector<char> ProgramErrorMessage(InfoLogLength + 1);
        glGetProgramInfoLog(ProgramID, InfoLogLength, NULL&ProgramErrorMessage[0]);
        printf("%s\n"&ProgramErrorMessage[0]);
    }
 
    glDetachShader(ProgramID, VertexShaderID);
    glDetachShader(ProgramID, FragmentShaderID);
 
    glDeleteShader(VertexShaderID);
    glDeleteShader(FragmentShaderID);
 
    return ProgramID;
}
 
GLuint loadBMP_custom(const char * imagepath) {
 
    printf("Reading image %s\n", imagepath);
 
    //BMP파일의 헤더에서 데이터를 읽는다
    unsigned char header[54];
    unsigned int dataPos;
    unsigned int imageSize;
    unsigned int width, height;
    //실제 RGB 데이터
    unsigned char * data;
 
    //파일을 연다
    FILE * file = fopen(imagepath, "rb");
    if (!file) {
        printf("%s는 열수 없다. 경로가 맞는지 확인해라.\n", imagepath);
        getchar();
        return 0;
    }
 
    //헤더를 읽는다, i.e. the 54 first bytes
 
    //만약 54 bytes보다 적게 읽혔으면 문제 발생
    if (fread(header, 154, file) != 54) {
        printf("BMP 파일이 아니다\n");
        return 0;
    }
    //A BMP 파일은 항상 "BM"으로 시작한다.
    if (header[0!= 'B' || header[1!= 'M') {
        printf("BMP 파일이 아니다\n");
        return 0;
    }
    //24pp file임을 확인한다.
    if (*(int*)&(header[0x1e]) != 0 || *(int*)&(header[0x1C]) != 24) {
        printf("BMP 파일이 아니다\n");
        return 0;
    }
 
    //이미지에 대한 정보를 읽는다.
    dataPos = *(int*)&(header[0x0A]);
    imageSize = *(int*)&(header[0x22]);
    width = *(int*)&(header[0x12]);
    height = *(int*)&(header[0x16]);
 
    //몇몇 BMP 파일들은 포맷이 놓쳐졌다, 놓쳐진 정보를 추측해라
    if (imageSize == 0) imageSize = width*height * 3// 3 : one byte for each Red-Green-Blue component
    if (dataPos == 0) dataPos = 54//BMP 헤더는 항상 이 형식
 
    //버퍼를 생성한다
    data = new unsigned char[imageSize];
 
    //파일의 버퍼에 있는 실제 데이터를 읽는다
    fread(data, 1, imageSize, file);
 
    //모든 것은 현재 메모리에 있다, 파일을 닫는다
    fclose(file);
 
    //openGL 텍스처를 만든다
    GLuint textureID;
    glGenTextures(1&textureID);
 
    //새로이 만들어진 텍스처를 바인딩한다.
    glBindTexture(GL_TEXTURE_2D, textureID);
 
    //이미지를 OpenGL에게 넘긴다
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
 
    delete[] data;
 
    // trilinear(삼선형) 필터링
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glGenerateMipmap(GL_TEXTURE_2D);
 
    return textureID;
}
 
GLuint loadDDS(const char * imagepath) {
 
    unsigned char header[124];
 
    FILE *fp;
 
    //파일을 연다
    fp = fopen(imagepath, "rb");
    if (fp == NULL) {
        printf("%s는 열 수 없다. 경로를 확인해라\n", imagepath);
        getchar();
        return 0;
    }
 
    //파일의 타입을 확인한다
    char filecode[4];
    fread(filecode, 14, fp);
    if (strncmp(filecode, "DDS "4!= 0) {
        fclose(fp);
        return 0;
    }
 
    //surface desc를 얻는다
    fread(&header, 1241, fp);
 
    unsigned int height = *(unsigned int*)&(header[8]);
    unsigned int width = *(unsigned int*)&(header[12]);
    unsigned int linearSize = *(unsigned int*)&(header[16]);
    unsigned int mipMapCount = *(unsigned int*)&(header[24]);
    unsigned int fourCC = *(unsigned int*)&(header[80]);
 
    unsigned char * buffer;
    unsigned int bufsize;
 
    bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;
    buffer = (unsigned char*)malloc(bufsize * sizeof(unsigned char));
    fread(buffer, 1, bufsize, fp);
    fclose(fp);
 
    unsigned int components = (fourCC == FOURCC_DXT1) ? 3 : 4;
    unsigned int format;
    switch (fourCC)
    {
    case FOURCC_DXT1:
        format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
        break;
    case FOURCC_DXT3:
        format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
        break;
    case FOURCC_DXT5:
        format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
        break;
    default:
        free(buffer);
        return 0;
    }
 
    //하나의 OpenGL 텍스처를 생성한다
    GLuint textureID;
    glGenTextures(1&textureID);
 
    //새로이 만들어진 텍스처를 바인딩한다
    glBindTexture(GL_TEXTURE_2D, textureID);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
    unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
    unsigned int offset = 0;
 
    //밉맵을 불러온다
    for (unsigned int level = 0; level < mipMapCount && (width || height); ++level)
    {
        unsigned int size = ((width + 3/ 4)*((height + 3/ 4)*blockSize;
        glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height,
            0size, buffer + offset);
 
        offset += size;
        width /= 2;
        height /= 2;
 
        //Non-Power-Of-Two 텍스처를 사용합니다.
        //이 코드는 혼란을 줄이기 위해 웹 페이지에는 포함되어 있지 않습니다.
        if (width < 1)width = 1;
        if (height < 1) height = 1;
    }
 
    free(buffer);
 
    return textureID;
}
 
 
bool loadOBJ(
    const char * path,
    std::vector<glm::vec3> & out_vertices,
    std::vector<glm::vec2> & out_uvs,
    std::vector<glm::vec3> & out_normals
) {
    printf("OBJ 파일 로딩중 %s...\n", path);
 
    std::vector<unsigned int> vertexIndices, uvIndices, normalIndices;
    std::vector<glm::vec3> temp_vertices;
    std::vector <glm::vec2> temp_uvs;
    std::vector<glm::vec3> temp_normals;
 
    FILE * file = fopen(path, "r");
    if (file == NULL) {
        printf("파일 경로를 확인하세요!\n");
        getchar();
        return false;
    }
 
    while (1) {
        
        char lineHeader[128];
 
        //첫번째 라인의 첫번째 단어를 읽는다
        int res = fscanf(file, "%s", lineHeader);
        if (res == EOF)
            break;
 
        //else : 라인의 헤더를 parse
        if (strcmp(lineHeader, "v"== 0) {
            glm::vec3 vertex;
            fscanf(file, "%f %f %f\n"&vertex.x, &vertex.y, &vertex.z);
            temp_vertices.push_back(vertex);
        }
        else if (strcmp(lineHeader, "vt"== 0) {
            glm::vec2 uv;
            fscanf(file, "%f %f\n"&uv.x, &uv.y);
            uv.y = -uv.y; //우리가 DDS texture만을 이용할 것이므로 V의 좌표를 반대로 바꾸어준다. 만약 TGA or BMP 로더를 사용하면 이 것을 제거해라.
            temp_uvs.push_back(uv);
        }
        else if (strcmp(lineHeader, "vn"== 0) {
            glm::vec3 normal;
            fscanf(file, "%f %f %f\n"&normal.x, &normal.y, &normal.z);
            temp_normals.push_back(normal);
        }
        else if (strcmp(lineHeader, "f"== 0) {
            std::string vertex1, vertex2, vertex3;
            unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
            int matches = fscanf(file,"%d/%d/%d %d/%d/%d %d/%d/%d\n"&vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2]);
            if (matches != 9) {
                printf("파일을 읽을수없다.");
                return false;
            }
            vertexIndices.push_back(vertexIndex[0]);
            vertexIndices.push_back(vertexIndex[1]);
            vertexIndices.push_back(vertexIndex[2]);
            uvIndices.push_back(uvIndex[0]);
            uvIndices.push_back(uvIndex[1]);
            uvIndices.push_back(uvIndex[2]);
            normalIndices.push_back(normalIndex[0]);
            normalIndices.push_back(normalIndex[1]);
            normalIndices.push_back(normalIndex[2]);
        }
        else {
            //나머지 라인을 먹는다.
            char stupidBuffer[1000];
            fgets(stupidBuffer, 1000, file);
        }
    }
 
    //각 삼각형의 각 꼭지점
    for (unsigned int i = 0; i < vertexIndices.size(); i++) {
        
        //속성의 인덱스를 가져온다
        unsigned int vertexIndex = vertexIndices[i];
        unsigned int uvIndex = uvIndices[i];
        unsigned int normalIndex = normalIndices[i];
 
        //인덱스에서 속성을 가져온다
        glm::vec3 vertex = temp_vertices[vertexIndex - 1];
        glm::vec2 uv = temp_uvs[uvIndex - 1];
        glm::vec3 normal = temp_normals[normalIndex - 1];
 
        //버퍼에 속성을 넣는다
        out_vertices.push_back(vertex);
        out_uvs.push_back(uv);
        out_normals.push_back(normal);
 
    }
 
    return true;
 
}
 
void computeMatricesFromInputs() {
 
    //glfwGetTime은 한번만 호출된다.
    static double lastTime = glfwGetTime();
 
    //현재와 마지막 프레임의 시간 차를 계산한다.
    double currentTime = glfwGetTime();
    float deltaTime = float(currentTime - lastTime);
 
    //마우스의 위치를 얻는다.
    double xpos, ypos;
    glfwGetCursorPos(window, &xpos, &ypos);
 
    //다음 프레임의 마우스 위치를 리셋한다.
    glfwSetCursorPos(window, 1024 / 2768 / 2);
 
    horizontalAngle += mouseSpeed * float(1024 / 2 - xpos);
    verticalAngle += mouseSpeed * float(768 / 2 - ypos);
 
    //Direction : Spherical 좌표 to Cartesian 좌표 변환
    glm::vec3 direction(
        cos(verticalAngle)*sin(horizontalAngle),
        sin(verticalAngle),
        cos(verticalAngle)*cos(horizontalAngle)
    );
 
    //Right vector
    glm::vec3 right = glm::vec3(
        sin(horizontalAngle - 3.14f / 2.0f),
        0,
        cos(horizontalAngle - 3.14f / 2.0f)
    );
 
    //Up vector
    glm::vec3 up = glm::cross(right, direction);
 
    //앞으로 이동
    if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
        position += direction*deltaTime*speed;
    }
    //뒤로 이동
    if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
        position -= direction*deltaTime*speed;
    }
    //오른쪽로 Strafe
    if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
        position += right*deltaTime*speed;
    }
    //왼쪽으로 Strafe
    if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
        position -= right*deltaTime*speed;
    }
 
    float FoV = initialFoV;
 
    ProjectionMatrix = glm::perspective(FoV, 4.0f / 3.0f, 0.1f, 100.0f);
 
    ViewMatrix = glm::lookAt(
        position,                //camera here
        position + direction,        //and looks here
        up                        // Head is up
    );
 
    //다음 프레임을 위해
    lastTime = currentTime;
}
cs




'Game > Graphics' 카테고리의 다른 글

OpenGL-Tutorial 10 : Transparency  (0) 2018.06.28
OpenGL-Tutorial 9 : VBO Indexing  (0) 2018.06.28
OpenGL-Tutorial 7 : Model loading  (0) 2018.06.22
OpenGL-Tutorial 6 : Keyboard and Mouse  (0) 2018.06.21
OpenGL-Tutorial 5 : A Textured Cube  (0) 2018.06.20