程序代写代做代考 gui c/c++ data structure Java GPU assembler javascript PowerPoint Presentation

PowerPoint Presentation

Getting Started with OpenGL:
Hello Triangle

Computer Graphics
Instructor: Sungkil Lee

2

Today

• Draw a colored triangle on the screen in a window

Overview

4

Event-Driven Programming

• OpenGL rendering is performed event-driven way

• Similar to GUI programming (e.g., Windows API, QT, Web)

• After initializations, the program enters an infinite event loop.

• The program is terminated with a TERM signal that user sent.

• See an example of Javascript-driven web programming

5

Event-Driven Programming

• An OpenGL program has a similar structure.

• GLFW registers callback functions for various window-related events.

• For each event, GLFW tries to call its registered callback function.

// event-driven callback functions
void update(){ … } // callback for per-frame update
void render(){ … } // callback for per-frame rendering
void reshape(){ … } // callback for window resizing
void keyboard(){ … } // callback for keyboard input
void mouse(){ … } // callback for mouse clicks
void motion(){ … } // callback for mouse movements

// init functions
void init_shaders(){ … }
void user_init(){ … }

// main function
int main(){ … }

6

Rendering Modes for OpenGL

• Immediate mode (old style)

• Put all vertex and attribute data in array

• Send array to GPU to be rendered immediately

• Problem is we would have to send the array over each time when
we need another render of it

• Do not use immediate mode for this course.

• Retained mode (modern style: better in performance)

• Send array over and store on GPU

• Reuse it for multiple renderings

• This is basic in modern-style GL.

7

OpenGL Pipeline in Retained Mode

Fragment
Shader

Vertex
Shader

gl_Position, varyings

Uniform
Variables

Attributes

gl_FragColor

glUniform

glBufferData

GPU
Memory

Vertex
Buffer

CPU
Memory

Vertex
Specification

Frame
Buffer

8

Preview of Shader Program

• Vertex shader in GLSL

• A vertex shader outputs the position of a single input vertex.

• It can also generate an additional output variable (e.g., vertex_color).

• Outputs will be passed as inputs to the next shaders.

in vec3 position; // vertex input attribute (you may use vec3/vec4 too)
in vec3 normal; // vertex input attribute
out vec3 vertex_color; // output of vertex shader

void main()
{

// gl_Position: builtin output variable that must be written as a screen position of vertex
gl_Position = vec4( position, 1 );

// another output passed via input variable
vertex_color = normal;

}

Vertex
Shader out

in
gl_Position

9

Preview of Shader Program

• Fragment shader in GLSL

• A fragment shader outputs the color of the single input pixel.

• fragColor is defined in [0,1], which maps later to [0,255] for 8 bpp color.

// the second input from vertex shader
in vec3 vertex_color;

// define output variable to be shown in the display
out vec4 fragColor;

// shader’s global variables, called the uniform variables
// Uniform variables will be shared among all the fragments (like a global variable).
uniform bool bUseSolidColor;
uniform vec4 solid_color;

void main()
{

// fragColor: you must specify it to produce color
fragColor = bUseSolidColor ? solid_color :vec4(vertex_color,1);

}

Fragment
Shader

Uniform Variables

in fragColor

10

Coordinate Systems in OpenGL

• Camera (eye-space) coordinate system

• OpenGL places a camera at the origin in object space pointing in the
negative z direction

• Use right-hand rule (RHS).

11

Coordinate Systems in OpenGL

• Normalized device coordinate (NDC) system

• Canonical view volume (the default viewing volume) is a box centered at
the origin with sides of 2: x: [-1,1], y: [-1,1], z: [-1,1]

• Be careful that NDC uses LHS convention for the definition

• Depth (z-axis) goes far from your eye

• This is intended for depth test, which maintains objects with the smallest
depths as visible.

• c.f., Eye-coordinate space uses the RHS convention .

(-1, -1, -1)

y

z

(0, 0, 0)

(1, 1, 1)

x

Closer look into Code

13

Common Headers

• cgmath.h (or cgmath-min.h)

• slee’s basic math library

• cgut.h

• slee’s OpenGL utility library

• defines common data structures

• defines many utility functions including:

struct vertex; // structure for vertices
struct mesh; // collection of vertices and indices for rendering

cg_create_window();
cg_init_extensions();
cg_create_program();
cg_destroy_window();

14

Global variable definitions

• All the examples will use variables similar to below.

// global constants
const char* window_name= “cgbase – Hello triangle!”;
const char* vert_shader_path = “../bin/shaders/hello.vert”;
const char* frag_shader_path = “../bin/shaders/hello.frag”;

// window objects
GLFWwindow* window = nullptr; // default GLFW window
ivec2 window_size = ivec2( 720, 720 ); // initial window size

// OpenGL objects holding IDs generated from OpenGL
GLuint program = 0; // ID holder for GPU program
GLuint vertex_buffer = 0; // ID holder for vertex buffer

// global variables
int frame = 0; // index of rendering frames
vec4 solid_color = vec4( 1.0f, 0.5f, 0.5f, 1.0f );
bool bUseSolidColor = false;

15

main()

• Initialization

• After initializing GLFW, window is created via cg_create_window()

• To retrieve function points of OpenGL extensions, call cg_init_extensions()

• OpenGL extensions are dynamically obtained from driver (OpenGL32.dll)

• Error checks are not shown here, but in the real example.

void main( int argc, char* argv[] )
{

// initialization
glfwInit();
window = cg_create_window( window_name, window_size.x, window_size.y );
cg_init_extensions( window ); // init OpenGL extensions

16

main()

• Initialization and validations of GLSL program

• cg_create_program() compiles vertex and fragment shaders and links
them together for a single GLSL program.

• See details in cgut.h

• user_init(): user-defined initialization

• will be explained later

// initializations and validations of GLSL program
program = cg_create_program( vert_shader_path, frag_shader_path );
user_init(); // user initialization

17

main()

• Registration of event callbacks

• glfwSet(*)Callback registers callback functions to the associated window
events.

• There are four types of callbacks:

• window resizing, keyboard press/release, mouse clicks, mouse movements

• Each callback function will be explained later.

// register event callbacks
glfwSetWindowSizeCallback( window, reshape ); // window resizing events
glfwSetKeyCallback( window, keyboard ); // keyboard events
glfwSetMouseButtonCallback( window, mouse ); // mouse click inputs
glfwSetCursorPosCallback( window, motion ); // mouse movements

18

main()

• Event loop

• When glfwWindowShouldClose() returns true, the loop is terminated.

• glfwPollEvents() processs events and their registered callbacks.

• User-defined update() and render() functions are called in a row.

• These functions are for per-frame update and rendering.

// enters rendering/event loop
for( frame=0; !glfwWindowShouldClose(window); frame++ )
{

glfwPollEvents(); // at this time, event callbacks are called
update(); // per-frame update
render(); // per-frame render

}

19

main()

• Termination

• user_finalize() do user-defined cleans.

• cg_destroy_window() calls glfwTerminate().

// normal termination
user_finalize();
cg_destroy_window(window);

}

20

user_init()

• User-defined initialization are placed here

• print usage of the applications

• initialize basic GL states

• clear color, back face culling, depth test, …

• define host-side vertex attributes

• here, a triangle of three vertices

• create vertex buffers

• This also copies CPU data to GPU, making available from GPU programs.

• any other things that you need to initialize

21

user_init()

• First, show usage on console

• This is highly recommended when you do not have text rendering.

• Initialize GL states

• This is nearly default for all the other examples

bool user_init()
{

// log hotkeys
print_help();

// init GL states
glClearColor( 39/255.0f, 40/255.0f, 34/255.0f, 1.0f ); // set clear color
glEnable( GL_CULL_FACE ); // turn on backface culling
glEnable( GL_DEPTH_TEST ); // turn on depth test


return true;

}

22

user_init()

• Vertex definition

• Here, we use norm to store vertex color (for compatibility with other
examples), though it’s intended for normal vectors.

• Create a vertex array on host (CPU) side

struct vertex // will be used for all the course examples
{

vec3 pos; // position
vec3 norm; // normal vector; use this for vertex color for this example
vec2 tex; // texture coordinate; ignore this for the moment

};


// create a vertex array for triangles in default view volume: [-1~1, -1~1, -1~1]
vertex vertices[] =
{

{ vec3(0.433f,-0.25f,0), vec3(1,0,0), vec2(0.0f) }, // {position, red color, …}
{ vec3(0.0f,0.5f,0), vec3(0,1,0), vec2(0.0f) }, // {position, green color, …}
{ vec3(-0.433f,-0.25f,0), vec3(0,0,1), vec2(0.0f) } // {position, blue color, …}

23

user_init()

• Create vertex buffers

• OpenGL buffer objects allow us to efficiently transfer large amounts of
data to the GPU.

• Vertex buffers are the input to the vertex shader of a GPU program.

• You can access it via buffer ID (e.g., vertex_buffer in the code).
• There is no way of directly accessing GL objects.

bool user_init()
{

// create and update vertex buffer
glGenBuffers(1, &vertex_buffer ); // generate a buffer object
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); // notify GL using the buffer

24

user_init()

• Allocate GPU buffer memory

• glBufferData() allocates GPU memory.

• A host-side vertex array is compiled to GPU vertex buffers.

• When nullptr is given, there is no data copy to GPU.

• Remember GL buffers are only the copies of host-side data.

• We use GL_STATIC_DRAW, since our example has constant content.

• When vertices are dynamic, use GL_DYNAMIC_DRAW, but dynamic buffers are
usually not recommend, because they incur significant data transfer overhead
between CPU and GPU.

// create and update vertex buffer
glBufferData( GL_ARRAY_BUFFER, sizeof(vertices), vertices,

GL_STATIC_DRAW ); // allocate GPU memory and copy vertices to it

25

user_finalize ()

• You can locate some clean-up, here.

• But, it’s empty at present.

void user_finalize()
{

// some clean-up code here
};

update() and render()

27

update()

• Update simulation

• Here, we compute rotation based on elapsed time.

• You may put more simulations and transformations here.

void update()
{

// update simulation factor by time
float theta = float(glfwGetTime())*0.5f;


}

28

update()

• Uniform variables

• Read-only global variables in GPU program, which can be accessed when
processing any vertices and fragments.

• Uniform variables are similar to the concept of global variables in C.

Fragment shaderVertex shader

out/in

Uniform
Variables

in gl_FragColor

CPU
Variables

glUniform

29

update()

• Update uniform variables

• First query the index of uniform variables using its name.

• Returning -1 means that there is no such name or is not used in the program.

• Declare but non-used variables also returns -1.

• glUniform*() copies CPU variables to GPU’s uniform variable objects.

void update()
{

// update uniform variables in vertex/fragment shaders
GLint uloc;

uloc = glGetUniformLocation( program, “theta” );
if(uloc>-1) glUniform1f( uloc, theta );

uloc = glGetUniformLocation( program, “bUseSolidColor” );
if(uloc>-1) glUniform1i( uloc, bUseSolidColor );

uloc = glGetUniformLocation( program, “solid_color” );
if(uloc>-1) glUniform4fv( uloc, 1, solid_color );

}

30

render()

• First, clear the framebuffer (screen) with the clear color

• Clear color was configured in user_init()

• Notify GL that we use our own program

void render()
{

// clear screen (with background color) and clear depth buffer
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

// notify GL that we use our own program
glUseProgram( program );


}

31

render()

• Bind vertex attributes to GPU program

• Now we need to let OpenGL know the structure of our vertex buffers

• First, define vertex attribute information of their names and memory size
in bytes.

// bind vertex attributes to your shader program
const char* vertex_attrib[] = { “position”, “normal”, “texcoord” };
size_t attrib_size[] =

{ sizeof(vertex().pos), sizeof(vertex().norm), sizeof(vertex().tex) };

32

render()

• Bind vertex attributes to GPU program

• glGetAttribLocation() retrieves the index of each attribute using its name in
vertex shader. If location is -1, it may not be used or not exist.

• After enabling the attribute using glEnableVertexAttribArray(), connect the
our vertex buffer object to the vertex attribute location using glBindBuffer()

for( size_t k=0,byte_offset=0; k<3; k++, byte_offset+=attrib_size[k-1] ) { GLuint loc = glGetAttribLocation( program, vertex_attrib[k] ); if(loc>=kn) continue; // -1 in GLuint == 0xFFFFFFFF

glEnableVertexAttribArray( loc );
glBindBuffer( GL_ARRAY_BUFFER, vertex_buffer );


}

}

33

render()

• Bind vertex attributes to GPU program

• We notify GL the memory layout of vertex buffer.

• Memory layout of C/C++ structures

glVertexAttribPointer(
loc, // attribute location
attrib_size[k]/sizeof(GLfloat), // number of elements (e.g., 3 for vec3)
GL_FLOAT, // type of elements (e.g., float for vec3)
GL_FALSE, // fixed-point normalization; never use it
sizeof(vertex), // stride: size of structure
(GLvoid*) byte_offset ); // byte offset of each attribute

pos

norm

vertices[0]

stride = sizeof(vertex)
= 32 bytes

byte_offset: 0

byte_offset: 12 = sizeof(vertex().pos)

byte_offset: 24 = sizeof(vertex().pos) + sizeof(vertex().norm)

tex

34

render()

• Finally trigger GPU program by calling

• glDrawArrays( GL_TRIANGLES, 0, 3 )

• Double-buffer swapping

• In double buffering, we are filling pixels in the back buffer, but we see the
image of the front buffer.

• glfwSwapBuffers() notifies to GLFW to swap back and front buffers.

• At this point, the system waits for vertical refresh of a monitor.

void render()
{

// render vertices: trigger shader programs to process vertex data
glDrawArrays(GL_TRIANGLES,0,3); // (topology, start offset, no. vertices)

// swap front and back buffers, and display to screen
glfwSwapBuffers( window );

}

Callback Functions

36

reshape()

• callback for when a window is resized.

void reshape( GLFWwindow* window, int width, int height )
{

// set current viewport in pixels (win_x, win_y, win_width, win_height)
// viewport: the window area that are affected by rendering
window_size = ivec2(width,height);
glViewport( 0, 0, width, height );

}

37

keyboard()

• callback for when a user pressed/released a key.

• You can handle all the low-level keyboard events here.

• key is defined by GLFW and action indicates the press/release

• mods are logical OR of ctrl/shift/alt modifier.

void keyboard( GLFWwindow* window, int key, int scancode, int action, int mods )
{

if(action==GLFW_PRESS)
{

if(key==GLFW_KEY_ESCAPE||key==GLFW_KEY_Q) glfwSetWindowShouldClose(window,GL_TRUE);
else if(key==GLFW_KEY_H||key==GLFW_KEY_F1) print_help();
else if(key==GLFW_KEY_D) …

}
else if(action==GLFW_RELEASE)
{


}

}

38

mouse()/motion()

• mouse(): callback for mouse button clicks

• button indicates which button is pressed/released.

• action can be either of GLFW_PRESS or GLFW_RELEASE

• The mouse position can be found using glfwGetCursorPos()

• motion(): callback for mouse movements

void mouse( GLFWwindow* window, int button, int action, int mods )
{

if( button==GLFW_MOUSE_BUTTON_LEFT && action==GLFW_PRESS )
{

dvec2 pos; glfwGetCursorPos( window, &pos.x, &pos.y );
printf( “> Left mouse button pressed at (%d, %d)\n”, int(pos.x), int(pos.y) );

}
}

void motion( GLFWwindow* window, double x, double y )
{
}

OpenGL Shader Programming

40

Shader Programs

• Create shader program

• “hello.vert” and “hello.frag” are the program sources of GPU programs.

• Programs are specified in terms of vertex and fragment shaders.

• Shader sources are complied/linked at run time.

// initializations and validations of GLSL program
program = cg_create_program( vert_shader_path, frag_shader_path );

Vertex
shader

Fragment
shader

Vertices Pixels
Primitive

assembler
Clipper Rasterizer

41

OpenGL Shader Execution Model

hello.vert

Application OpenGL Driver

Shader object

Compile

hello.frag
Program object

Linker

Graphics HW

Provided by application developer using OpenGL API

Provided by graphics hardware vender (NVIDIA, AMD, and Intel)

Shader object

42

Vertex and Fragment Shaders

• A vertex processor transforms a single input vertex at a time.

• But, this is also performed in parallel by multiple vertex processors.

• Then, they are combined to primitives (here, to triangles).

• Then, the rasterizer converts the primitives to pixels on the screen.

• A fragment processor processes a single fragment at a time.

• This is also performed in parallel by multiple fragment processors.

Vertex
shader

Fragment
shader

Vertices Pixels
Primitive

assembler
Clipper Rasterizer

43

Example Data Flow: Revisited

vec3(0.5,-0.5,0)
vec3(1,0,0)
vec2(0,0)

vec3(-0.5,-0.5,0)
vec3(0,0,1)
vec2(0,0)

vec3(0,0.6,0)
vec3(0,1,0)
vec2(0,0)

fragment fragment fragment fragment… fragment

vertex vertex vertex

Vertex
shader

Fragment
shader

Primitive
assembler

Clipper

Rasterizer

Vertex
shader

Vertex
shader

Fragment
shader

Fragment
shader

Fragment
shader

Fragment
shader

44

Vertex Shader: hello.vert

• A vertex shader outputs the position of the single vertex

#version 130 // use “300 es” or “320 es” for OpenGL ES

// input attributes of vertices
in vec3 position;
in vec3 normal; // we are using this for vertex color
// the second output = input to fragment shader
out vec3 vertex_color;
// uniform variables
uniform float theta;

void main()
{

// gl_Position: a built-in output variable that should be written in main()
gl_Position = vec4( position, 1 );
// rotation vector
vec2 r = vec2(cos(theta),sin(theta));
// rotate the vertices
gl_Position.x = dot( position.xy, vec2(r.x,-r.y) );
gl_Position.y = dot( position.xy, r.yx ); // swizzling for easy access
// another output passed via varying variable
vertex_color = normal; // pass the color in norm to the vertex color output

}

45

Fragment Shader: hello.frag

• A fragment shader outputs the color of the single input pixel.

• Pixel output (fragColor) is defined in [0,1], which maps later to [0,255] for
8-bit color depth.

#version 130 // use “300 es” or “320 es” for OpenGL ES

// the second input from vertex shader
in vec3 vertex_color;

// define pixel output variable instead of old-style gl_FragColor
out vec4 fragColor;

// shader’s global variables, called the uniform variables
uniform bool bUseSolidColor;
uniform vec4 solid_color;

void main()
{

fragColor = bUseSolidColor ? solid_color : vec4(vertex_color,1);
}

46

Fragment Shader: hello.frag

• (OpenGL ES only) Precision modifier:

• We may use faster arithmetics for floating-point numbers by providing
hints in vertex/fragment shaders .

• This is quite useful for OpenGL ES for mobile platform.

• Possible options and usage:

• highp: vertex positions

• mediump: normal vectors / texture coordinates

• lowp: colors

precision mediump float;

47

Final Output

Any questions on GL or GLSL?