• Home
  • Textbooks
  • Mathematics for Game Programming and Computer Graphics: Explore the essential mathematics for creating, rendering, and manipulating 3D virtual environments
  • Vertex and Fragment Shading

Mathematics for Game Programming and Computer Graphics: Explore the essential mathematics for creating, rendering, and manipulating 3D virtual environments

Penny de Byl

Chapter 17

Vertex and Fragment Shading - all with Video Answers

Educators


Section 1

Understanding shaders

Problem 1

Make a copy of the Chapter_16 folder, and name it Chapter_17.

Check back soon!

Problem 2

For OpenGL to process shader code, it must process and compile it for the GPU. To do this, we need to write a dedicated function. Open Utils.py. It will only have the map_value () function in it. Add the following code:
from OpenGL.GL import *
import numpy as np
def map_value(current_min, current_max, new_min,
new_max, value) :
def compile_shader(shader_type, shader_source):
shader_id = glCreateShader(shader_type)
glShaderSource(shader_id, shader_source)
glCompileShader(shader_id)
compile_success = glGetShaderiv(shader_id,
GL_COMPILE_STATUS)
if not compile_success:
error_message = glGetShaderInfoLog(shader_id)
glDeleteShader(shader_id)
error_message = “\n” +
error_message.decode(“utf-8”)
raise Exception(error_message)
return shader_id

Check back soon!

Problem 3

Shaders are linked to models by the use of materials. A material becomes a component of a mesh, just like a transform. It specifies how the mesh is to be rendered. This means that you can select which shader to use on a model as well as change it. Different models can have different shaders. Create a new Python script called Material py and add the following code to it:
from Utils import *
class Material:
def__init_(self, vertex_shader,
fragment_shader) :
self $\cdot$ program_id = create_program (
open (vertex_shader).read (),
open (fragment_shader).read ())
def use (self):
glUseProgram(self.program_id)

Check back soon!

Problem 4

To use shaders in our project, the main script needs to be changed. Create a new Python script called ShaderTeapot. py. All of the following code will be new to this file, though I have presented the shader-specific code in bold for you to take extra note of as you type it out:
from Object import *
from pygame.locals import *
from Camera import *
from LoadMesh import *
from Material import *
from Settings import *
pygame.init ()
pygame.display.gl_set_attribute (
pygame.GL_MULTISAMPLEBUFFERS,1)
pygame.display.gl_set_attribute (
pygame.GL_MULTISAMPLESAMPLES, 4)
pygame.display.gl_set_attribute (
pygame.GL_CONTEXT_PROFILE_MASK,
pygame.GL_CONTEXT_PROFILE_CORE)
pygame.display.gl_set_attribute (pygame.GL_DEPTH_SIZE,
32)

Check back soon!

Problem 5

Next, we create two helper classes to allocate and structure memory to hold the data to be used by the shaders. The shader code we will write will replace all the OpenGL drawing code we've written to date. In some ways, the shader code will also simplify the OpenGL drawing code, but to work, the shaders still need to know about vertices, colors, projection and view matrices, and other data. The first class we create will be in a script called GraphicsData. py. Create this file and add the following code to it:
from OpenGL.GL import *
import numpy as np
class GraphicsData():
def_init_(self, data_type, data):
self.data_type = data_type
self.data $=$ data
self.buffer_ref = glGenBuffers(1)
self.load()
def load(self) :
data $=\mathrm{np} \cdot \operatorname{array}($ self.data, np.float32)
glBindBuffer(GL_ARRAY_BUFFER, self.buffer_ref)
glBufferData (GL_ARRAY_BUFFER, data.ravel (),
GL_STATIC_DRAW)
def create_variable(self, program_id,
variable_name) :
variable_id = glGetAttribLocation(program_id,
variable_name)
glBindBuffer(GL_ARRAY_BUFFER, self.buffer_ref)
if self.data_type $==$ "vec3":
glVertexAttribPointer(variable_id, 3,
GL_FLOAT,
False, 0, None)
elif self.data_type == "vec2" :
glVertexAttribPointer(variable_id, 2,
GL_FLOAT,
False, 0, None)
glEnableVertexAttribArray (variable_id)

Check back soon!

Problem 6

Variable values that can change and be passed to a shader are called uniforms. Create a new Python script called Uni form. py and add the following code to it:
from OpengL.GL import *
class Uniform():
def__init_(self, data_type, data) :
self . data_type $=$ data_type
self $\cdot$ data $=$ data
self.variable_id = None
def find_variable(self, program_id,
variable_name):
self.variable_id =
glGetUniformLocation(program_id,
variable_name)
def load(self):
if self.data_type == “vec3”:
glUniform3f(self.variable_id,
self.data[0],
self.data[1], self.data[2])
elif self.data_type == “mat4”:
glUniformMatrix4fv(self.variable_id, 1,
GL_TRUE,
self.data)
elif self.data_type == “sampler2D”:
texture_obj, texture_unit = self.data
glActiveTexture(GL_TEXTURE0 +
texture_unit)
glBindTexture(GL_TEXTURE_2D, texture_obj)
glUniform1i(self.variable_id,
texture_unit)

Check back soon!