def drawObjModel(viewToClipTfm, worldToViewTfm, modelToWorldTfm, model): # Lighting/Shading is very often done in view space, which is why a transformation that lands positions in this space is needed modelToViewTransform = worldToViewTfm * modelToWorldTfm # this is a special transform that ensures that normal vectors remain orthogonal to the # surface they are supposed to be even in the prescence of non-uniform scaling. # It is a 3x3 matrix as vectors don't need translation anyway and this transform is only for vectors, # not points. If there is no non-uniform scaling this is just the same as Mat3(modelToViewTransform) modelToViewNormalTransform = lu.inverse( lu.transpose(lu.Mat3(modelToViewTransform))) # Bind the shader program such that we can set the uniforms (model.render sets it again) glUseProgram(model.defaultShader) # transform (rotate) light direction into view space (as this is what the ObjModel shader wants) viewSpaceLightDirection = [0.0, 0.0, -1.0] glUniform3fv( glGetUniformLocation(model.defaultShader, "viewSpaceLightDirection"), 1, viewSpaceLightDirection) # This dictionary contains a few transforms that are needed to render the ObjModel using the default shader. # it would be possible to just set the modelToWorld transform, as this is the only thing that changes between # the objects, and compute the other matrices in the vertex shader. # However, this would push a lot of redundant computation to the vertex shader and makes the code less self contained, # in this way we set all the required parameters explicitly. transforms = { "modelToClipTransform": viewToClipTfm * worldToViewTfm * modelToWorldTfm, "modelToViewTransform": modelToViewTransform, "modelToViewNormalTransform": modelToViewNormalTransform, } model.render(None, None, transforms)
def setCommonUniforms(self, shader, view, modelToWorldTransform): # Concatenate the transformations to take vertices directly from model space to clip space modelToClipTransform = view.viewToClipTransform * view.worldToViewTransform * modelToWorldTransform # Transform to view space from model space (used for the shading) modelToViewTransform = view.worldToViewTransform * modelToWorldTransform # Transform to view space for normals, need to use the inverse transpose unless only rigid body & uniform scale. modelToViewNormalTransform = lu.inverse( lu.transpose(lu.Mat3(modelToViewTransform))) # Set the standard transforms, these vary per object and must be set each time an object is drawn (since they have different modelToWorld transforms) lu.setUniform(shader, "modelToClipTransform", modelToClipTransform) lu.setUniform(shader, "modelToViewTransform", modelToViewTransform) lu.setUniform(shader, "modelToViewNormalTransform", modelToViewNormalTransform) # These transforms are the same for the current view and could be set once for all the objects lu.setUniform(shader, "worldToViewTransform", view.worldToViewTransform) lu.setUniform(shader, "viewToClipTransform", view.viewToClipTransform) # Lighting parameters as could these viewSpaceLightPosition = lu.transformPoint(view.worldToViewTransform, g_sunPosition) lu.setUniform(shader, "viewSpaceLightPosition", viewSpaceLightPosition) lu.setUniform(shader, "globalAmbientLight", g_globalAmbientLight) lu.setUniform(shader, "sunLightColour", g_sunLightColour) lu.bindTexture(g_TU_shadow, g_shadowTexId) lu.setUniform(shader, "shadowMapTexture", g_shadowTexId)
def update(self, dt, keyStateMap): info = self.terrain.getInfoAt(self.position) # Select max speed based on material maxSpeed = self.maxSpeedRoad if info.material == TerrainInfo.M_Road else self.maxSpeedRough targetVel = vec3(0.0) if keyStateMap["UP"]: targetVel = self.heading * maxSpeed if keyStateMap["DOWN"]: targetVel = self.heading * -maxSpeed # linearly interpolate towards the target velocity - this means it is tied to the frame rate, which kind of is bad. self.velocity = lu.mix(self.velocity, targetVel, 0.01) self.speed = lu.length(self.velocity) rotationMat = lu.Mat4() if keyStateMap["LEFT"]: rotationMat = lu.make_rotation_z(dt * self.angvel) if keyStateMap["RIGHT"]: rotationMat = lu.make_rotation_z(dt * -self.angvel) self.heading = lu.Mat3(rotationMat) * self.heading # get height of ground at this point. self.position += self.velocity * dt # TODO 1.1: After the terrain height is correct, uncomment this line to make the racer follow the ground height self.position[2] = lu.mix(self.position[2], info.height + self.zOffset, 0.1)
def update(dt, keyStateMap, mouseDelta): global g_sunPosition global g_sunAngle global g_globalAmbientLight global g_sunLightColour global g_sunAngle global g_updateSun global g_viewTarget global g_viewPosition global g_followCamOffset global g_followCamLookOffset if g_updateSun: g_sunAngle += dt * 0.25 g_sunAngle = g_sunAngle % (2.0 * math.pi) g_sunPosition = lu.Mat3( lu.make_rotation_x(g_sunAngle)) * g_sunStartPosition g_sunLightColour = sampleKeyFrames( lu.dot(lu.normalize(g_sunPosition), vec3(0.0, 0.0, 1.0)), g_sunKeyFrames) g_globalAmbientLight = sampleKeyFrames( lu.dot(lu.normalize(g_sunPosition), vec3(0.0, 0.0, 1.0)), g_ambientKeyFrames) g_racer.update(dt, keyStateMap) # TODO 1.2: Make the camera look at the racer. Code for updating the camera should be done after the # racer, otherwise the offset will lag and it generally looks weird. if imgui.tree_node("Camera", imgui.TREE_NODE_DEFAULT_OPEN): _, g_followCamOffset = imgui.slider_float("FollowCamOffset ", g_followCamOffset, 2.0, 100.0) _, g_followCamLookOffset = imgui.slider_float("FollowCamLookOffset", g_followCamLookOffset, 0.0, 100.0) imgui.tree_pop() if imgui.tree_node("Racer", imgui.TREE_NODE_DEFAULT_OPEN): g_racer.drawUi() imgui.tree_pop() if imgui.tree_node("Terrain", imgui.TREE_NODE_DEFAULT_OPEN): g_terrain.drawUi() imgui.tree_pop() if imgui.tree_node("Lighting", imgui.TREE_NODE_DEFAULT_OPEN): _, g_globalAmbientLight = lu.imguiX_color_edit3_list( "GlobalAmbientLight", g_globalAmbientLight ) #, imgui.GuiColorEditFlags_Float);// | ImGuiColorEditFlags_HSV); _, g_sunLightColour = lu.imguiX_color_edit3_list( "SunLightColour", g_sunLightColour ) #, imgui.GuiColorEditFlags_Float);// | ImGuiColorEditFlags_HSV); _, g_sunAngle = imgui.slider_float("SunAngle", g_sunAngle, 0.0, 2.0 * math.pi) _, g_updateSun = imgui.checkbox("UpdateSun", g_updateSun) imgui.tree_pop()
def renderFrame(uiWidth, width, height): global g_triangleVerts global g_cameraDistance global g_cameraYawDeg global g_cameraPitchDeg global g_yFovDeg global g_lightYaw global g_lightDistance global g_lightPitch # This configures the fixed-function transformation from Normalized Device Coordinates (NDC) # to the screen (pixels - called 'window coordinates' in OpenGL documentation). # See: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glViewport.xhtml glViewport(0, 0, width, height) # Set the colour we want the frame buffer cleared to, glClearColor(0.2, 0.5, 0.1, 1.0) # Tell OpenGL to clear the render target to the clear values for both depth and colour buffers (depth uses the default) glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT) viewToClipTransform = lu.make_perspective(g_yFovDeg, width / height, 0.1, 50.0) eyePos = lu.Mat3(lu.make_rotation_y( math.radians(g_cameraYawDeg))) * lu.Mat3( lu.make_rotation_x( math.radians(g_cameraPitchDeg))) * [0, 0, g_cameraDistance] worldToViewTransform = magic.make_lookAt(eyePos, [0, 0, 0], [0, 1, 0]) worldToClipTransform = viewToClipTransform * worldToViewTransform lightRotation = lu.Mat3(lu.make_rotation_y( math.radians(g_lightYaw))) * lu.Mat3( lu.make_rotation_x(math.radians(g_lightPitch))) lightPosition = [0.02, 0, 0] + lu.vec3(0, 23, 0.2) draw_court(worldToClipTransform, lightPosition) lu.drawSphere([0.23, -0.45, 0.1, 0], 0.007, [0, 1, 0, 0.5], viewToClipTransform, worldToViewTransform) lu.drawSphere(lightPosition, 0.01, [1, 1, 0, 1], viewToClipTransform, worldToViewTransform)
def render(self, shaderProgram=None, renderFlags=None, transforms={}): if not renderFlags: renderFlags = self.RF_All if not shaderProgram: shaderProgram = self.defaultShader # Filter chunks based of render flags chunks = [ch for ch in self.chunks if ch[3] & renderFlags] glBindVertexArray(self.vertexArrayObject) glUseProgram(shaderProgram) # define defaults (identity) defaultTfms = { "modelToClipTransform": lu.Mat4(), "modelToViewTransform": lu.Mat4(), "modelToViewNormalTransform": lu.Mat3(), } # overwrite defaults defaultTfms.update(transforms) # upload map of transforms for tfmName, tfm in defaultTfms.items(): loc = magic.getUniformLocationDebug(shaderProgram, tfmName) tfm._set_open_gl_uniform(loc) previousMaterial = None for material, chunkOffset, chunkCount, renderFlags in chunks: # as an optimization we only do this if the material has changed between chunks. # for more efficiency still consider sorting chunks based on material (or fusing them?) if material != previousMaterial: previousMaterial = material if self.overrideDiffuseTextureWithDefault: bindTexture(self.TU_Diffuse, self.defaultTextureOne, self.defaultTextureOne) else: bindTexture(self.TU_Diffuse, material["texture"]["diffuse"], self.defaultTextureOne) bindTexture(self.TU_Opacity, material["texture"]["opacity"], self.defaultTextureOne) bindTexture(self.TU_Specular, material["texture"]["specular"], self.defaultTextureOne) bindTexture(self.TU_Normal, material["texture"]["normal"], self.defaultNormalTexture) # TODO: can I do uniform buffers from python (yes, I need to use that struct thingo!) #uint32_t matUniformSize = sizeof(MaterialProperties_Std140); #glBindBufferRange(GL_UNIFORM_BUFFER, UBS_MaterialProperties, m_materialPropertiesBuffer, (uint32_t)chunk.material->offset * matUniformSize, matUniformSize); # TODO: this is very slow, it should be packed into an uniform buffer as per above! for k, v in material["color"].items(): glUniform3fv( magic.getUniformLocationDebug(shaderProgram, "material_%s_color" % k), 1, v) glUniform1f( magic.getUniformLocationDebug( shaderProgram, "material_specular_exponent"), material["specularExponent"]) glUniform1f( magic.getUniformLocationDebug(shaderProgram, "material_alpha"), material["alpha"]) glDrawArrays(GL_TRIANGLES, chunkOffset, chunkCount) glUseProgram(0)
def render_frame(width, height): """ Renders the frame """ global g_shader global g_squares global g_square_colors global g_square_normals global g_cam global g_light_1 global g_light_color_1 global g_ambi_color global g_spec_color # Make the camera position eye_pos = g_cam look_at = [1.5, 1.5, -1.5] up_dir = [0, 1, 0] y_fov = 45 # Light constants ambient_light = g_ambi_color specular_light = g_spec_color world_to_view = lu.make_lookAt(eye_pos, look_at, up_dir) view_to_clip = lu.make_perspective(y_fov, width / height, 0.1, 260) world_to_clip = view_to_clip * world_to_view model_to_view_normal = lu.inverse(lu.transpose(lu.Mat3(world_to_view))) # Make openGL use transform from screen space to NDC glViewport(0, 0, width, height) # Set the clear colour (i.e. background colour) glClearColor(0.7, 0.8, 1.0, 1.0) make_squares() make_texture_coords() # Unbind the buffers glBindBuffer(GL_ARRAY_BUFFER, 0) glBindVertexArray(0) # Clear the colour and depth buffers glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT) # Buffer the world to clip transform matrix tfm_uniform_index = glGetUniformLocation(g_shader, "worldToClipTfm") glUseProgram(g_shader) glUniformMatrix4fv(tfm_uniform_index, 1, GL_TRUE, world_to_clip.getData()) glBindVertexArray(g_vertex_array) model_uniform_index = glGetUniformLocation(g_shader, "modelToView") glUseProgram(g_shader) glUniformMatrix4fv(model_uniform_index, 1, GL_TRUE, world_to_view.getData()) norm_uniform_index = glGetUniformLocation(g_shader, "modelToViewNormal") glUseProgram(g_shader) glUniformMatrix3fv(norm_uniform_index, 1, GL_TRUE, model_to_view_normal.getData()) lu.setUniform(g_shader, "camPos", eye_pos) # Add light 1 uniforms lu.setUniform(g_shader, "lightColourAndIntensity1", g_light_color_1) lu.setUniform(g_shader, "viewSpaceLightPosition1", g_light_1) # Add light 2 uniforms lu.setUniform(g_shader, "lightColourAndIntensity2", g_light_color_2) lu.setUniform(g_shader, "viewSpaceLightPosition2", g_light_2) # Add light 3 uniforms lu.setUniform(g_shader, "lightColourAndIntensity3", g_light_color_3) lu.setUniform(g_shader, "viewSpaceLightPosition3", g_light_3) # Add light 4 uniforms lu.setUniform(g_shader, "lightColourAndIntensity4", g_light_color_4) lu.setUniform(g_shader, "viewSpaceLightPosition4", g_light_4) lu.setUniform(g_shader, "ambientLightColourAndIntensity", ambient_light) lu.setUniform(g_shader, "materialSpecular", specular_light) # Fog settings lu.setUniform(g_shader, "fogColor", g_fog_color) lu.setUniform(g_shader, "fogDensityConst", g_fog_density) lu.setUniform(g_shader, "fogHeightConst", g_fog_height) lu.setUniform(g_shader, "fogMax", g_fog_max) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, g_texture_id) loc = glGetUniformLocation(g_shader, "plasticTexture") glUniform1i(loc, 0) for i in range(int(len(g_squares) / 4)): lu.setUniform(g_shader, "squareColorIndex", g_square_colors[i]) lu.setUniform(g_shader, "squareNormal", g_square_normals[i]) glDrawArrays(GL_TRIANGLE_FAN, i * 4, 4)