<jupyter_start><jupyter_text>Ex9 - IluminaçãoNesta atividade, vocês vão exercitar os conceitos alguns dos conceitos de iluminação. Para evitar erros de execução, utilize apenas uma célula de código para cada parte desta atividade. Crie uma cena escura contendo terreno e outros objetos, e um objeto não afetado pela iluminação representando a fonte de luz. Utilize as teclas do teclado para controlar a posição deste objeto e, consequentemente, da fonte de luz. Mais especificamente, utilize as setas esquerda e direita para deslocar no eixo x, e as setas 'para cima' e 'para baixo' deslocam no eixo y. Além disso, utilize as teclas + e - para deslocar no eixo z. Para iluminar com mais intensidade apenas os objetos que estão mais próximo da fonte de luz e deixar os objetos mais distance mais escuros, tente alterar os parâmetros 'constante', 'linear' e 'exponencial' da equação de atenuação (figura a baixo, em que L_1 = intensidade da luz, L_distance = intensidade atenuada) através, respectivamente, dos métodos 'setUniformLightConstantAttenuation', 'setUniformLightLinearAttenuation' e 'setUniformLightQuadraticAttenuation' da classe PhongShadingShaderProgram. Uma vez que será preciso renderizar objetos com e sem iluminação, será preciso utilizar mais de um shader no mesmo programa. Veja no notebook ([32_Esferas_com_diferentes_shader_programs](32_Esferas_com_diferentes_shader_programs.ipynb)) como isso pode ser feito.   Parte 1 - Cena com fonte de luz pontualPrograme a cena descrita acima utilizando uma fonte de luz pontual ([33_Terreno_com_esferas_iluminacao_fonte_de_luz_pontual](33_Terreno_com_esferas_iluminacao_fonte_de_luz_pontual.ipynb)). As imagens abaixo ilustram o efeito esperado ao movimentar a fonte de luz pela cena.                                      In the following code, we will use our knowledge of lighting to iluminate a scene with spheres and a terrain. We include a sphere (unaffected by the ilumination) to represent our source of light and tune our attenuation parameters for the light to iluminate only close-by objects. Also, we include a key control, to control the position of our light source with the arrow and +- keys.<jupyter_code>import glm
import numpy as np
import math
import time
import OpenGL.GL as gl
from PyQt5 import QtOpenGL, QtCore
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtWidgets import QApplication

from cg.shader_programs.PhongShadingShaderProgram import PhongShadingShaderProgram
from cg.shader_programs.SimpleShaderProgram_v2 import SimpleShaderProgram
from cg.renderers.ModelRenderer_v3 import ModelRenderer
from cg.models.TerrainMesh_v1 import TerrainMesh
from cg.models.SphereMesh_v2 import SphereMesh

class MyWidget(QtOpenGL.QGLWidget):
    def initializeGL(self):
        
        self.cameraPos   = glm.vec3(0.0, 3.0, 1.5)
        self.cameraFront = glm.vec3(0.0, 0.0, 0.0)
        self.cameraUp    = glm.vec3(0.0, 1.0,  0.0)
    
        self.viewMatrix = glm.lookAt(self.cameraPos, self.cameraFront, self.cameraUp)
        
        self.lightPosition = glm.vec4(0.0, 0.5, 0.0, 1)
        
        # create spheric triangle mesh with 0.07 radius 
        sphere_mesh = SphereMesh(0.07, 20, 20)
        
        # create terrain mesh size (2,2,0.5) 
        # mesh is created at the origin and paralel to the XY plane (2,2) with a depth of 0.5
        # the position of a coordinate of the terrain grid
        # can be determined wtih the getPositionAt(row, col) method
        self.terrainMesh = TerrainMesh('cg/images/heightmap_1.png', 2.0, 2.0, 0.5, 300, 300)
        
        # create the objects responsible for loading data onto the GPU
        self.terrainRenderer = ModelRenderer(self.terrainMesh.getVertexPositions(),
                                             vertex_indices=self.terrainMesh.getVertexIndices(),
                                             vertex_normal=self.terrainMesh.getVertexNormals())
        
        self.phongSphereRenderer = ModelRenderer(sphere_mesh.getVertexPositions(),
                                             vertex_indices=sphere_mesh.getVertexIndices(),
                                             vertex_normal=sphere_mesh.getVertexNormals())
        
        self.simpleSphereRenderer = ModelRenderer(sphere_mesh.getVertexPositions(),
                                            vertex_indices=sphere_mesh.getVertexIndices(),
                                            vertex_normal=sphere_mesh.getVertexNormals())
        
        # create Phong ilumination shader for all objects except light       
        # point light is the default source type
        self.phongShaderProgram = PhongShadingShaderProgram()
        
        # create simple shader for the light source object
        self.simpleShaderProgram = SimpleShaderProgram()
        
        # configure phong shader
        self.phongShaderProgram.bind()
        self.phongShaderProgram.useUniformMaterialColor(True) # tem o mesmo funcionamento da função useUniformColor do SimpleShader
        self.phongShaderProgram.setUniformLightPosition(self.viewMatrix * self.lightPosition) #a posição da luz tem que estar no espaço de visão
        self.phongShaderProgram.release()

        # configure simple shader
        self.simpleShaderProgram.bind()
        self.simpleShaderProgram.useUniformColor(True)
        self.simpleShaderProgram.release()
        
        # set model data as inputs for the phong shader
        position_loc = self.phongShaderProgram.getVertexPositionLoc()
        normal_loc = self.phongShaderProgram.getVertexNormalLoc()
        self.phongSphereRenderer.setVertexPositionLoc(position_loc)
        self.phongSphereRenderer.setVertexNormalLoc(normal_loc)
        
        # set model data as inputs for the simple shader
        position_loc = self.simpleShaderProgram.getVertexPositionLoc()
        self.simpleSphereRenderer.setVertexPositionLoc(position_loc)
        
        # configure terrain data for input
        self.terrainRenderer.setVertexPositionLoc(position_loc)
        self.terrainRenderer.setVertexNormalLoc(normal_loc)

        # enable depth testing and culling
        gl.glEnable(gl.GL_DEPTH_TEST);
        gl.glEnable(gl.GL_CULL_FACE)
        
        # initialize sphere animation variables
        # (store terrain coordinates for each sphere)
        self.terrain_index = np.array([[150, 150], [150, 150], [150, 150]], dtype=np.int32)
        self.aux_index = np.array([[150, 150], [150, 150], [150, 150]], dtype=np.int32)
        self.index_offset = np.array([[0, 0], [0, 0], [0, 0]], dtype=np.int32)
        
        # store program start time
        self.startTime = time.time()
        
        # set global transformation
        self.globalTransformation = glm.rotate(glm.mat4(), glm.radians(-90), glm.vec3(1.0, 0.0, 0.0))       
        
        # enable color blending
        gl.glEnable(gl.GL_BLEND)
        
        # configure color blend
        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        
    def paintGL(self):
        
        # set background color
        gl.glClearColor(0.3, 0.3, 0.3, 1)
        
        # clean background with the specified color and depth buffer
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        
        # calculate current program time
        self.currentTime = time.time()
        time_difference = self.currentTime - self.startTime
        
        # update terrain coordinates for spheres
        if(time_difference > 3):
            self.startTime = self.currentTime
            time_difference = 0
            self.aux_index = self.terrain_index
            self.index_offset = np.random.randint(-100, high=100, size=(3,2))
            
        self.terrain_index = self.aux_index + (time_difference / 3.0) * self.index_offset
        self.terrain_index = np.clip(self.terrain_index, 0, 300)
        
        # activate the phong shader
        self.phongShaderProgram.bind()
        
        # set viewport
        gl.glViewport(0, 0, int(self.width), self.height)
        
        # render cene with light attenuation
        self.phongShaderProgram.useUniformLightAttenuation(True)
        self.phongShaderProgram.setUniformLightConstantAttenuation(0.0)
        self.phongShaderProgram.setUniformLightLinearAttenuation(1.3)
        self.phongShaderProgram.setUniformLightQuadraticAttenuation(8.0)
        self.renderTerrain()
        self.renderMovingSpheres()
        self.renderStaticSpheres()
        self.phongShaderProgram.release()
        
        # render light source sphere
        self.simpleShaderProgram.bind()
        self.renderLight()
        self.simpleShaderProgram.release()
        
        # re-run paintGL
        self.update()
        
    def renderTerrain(self):
        
        # calculate the model-view for the terrain
        self.phongShaderProgram.setUniformMVPMatrix(self.perspectiveMatrix * self.viewMatrix * self.globalTransformation)
        self.phongShaderProgram.setUniformModelViewMatrix(self.viewMatrix * self.globalTransformation)
        
        # set terrain properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([0.5, 0.25, 0.0, 1.0]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.8, 0.8, 0.8]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.8, 0.8, 0.8]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.1, 0.1, 0.1]))
        self.terrainRenderer.render()
    
    
    def renderMovingSpheres(self):
        
        # set gray sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 1.0]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.23125, 0.23125, 0.23125]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.2775, 0.2775, 0.2775]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.773911, 0.773911, 0.773911]))
        self.phongShaderProgram.setUniformMaterialShininess(89.6)
        
        # calculate gray sphere model-view matrix
        sphere_position = self.terrainMesh.getPositionAt(int(self.terrain_index[0, 0]), int(self.terrain_index[0, 1]))
        model_matrix = glm.translate(glm.mat4(), glm.vec3(sphere_position[0], sphere_position[1], sphere_position[2] + 0.07))
        mv_matrix = self.viewMatrix * self.globalTransformation * model_matrix
        self.renderSphere(mv_matrix)
        
        # set red sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 1.0]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.1745, 0.01175, 0.01175]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.61424, 0.04136, 0.04136]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.727811, 0.626959, 0.626959]))
        self.phongShaderProgram.setUniformMaterialShininess(76.8)
        
        # calculate red sphere model-view matrix
        sphere_position = self.terrainMesh.getPositionAt(int(self.terrain_index[1, 0]), int(self.terrain_index[1, 1]))
        model_matrix = glm.translate(glm.mat4(), glm.vec3(sphere_position[0], sphere_position[1], sphere_position[2] + 0.07))
        mv_matrix = self.viewMatrix * self.globalTransformation * model_matrix
        self.renderSphere(mv_matrix)
        
        # set orange sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 1.0]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.2125, 0.1275, 0.054]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.714, 0.4284, 0.18144]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.393548, 0.271906, 0.166721]))
        self.phongShaderProgram.setUniformMaterialShininess(25.6)
        
        # calculate orange sphere model-view matrix
        sphere_position = self.terrainMesh.getPositionAt(int(self.terrain_index[2, 0]), int(self.terrain_index[2, 1]))
        model_matrix = glm.translate(glm.mat4(), glm.vec3(sphere_position[0], sphere_position[1], sphere_position[2] + 0.07))
        mv_matrix = self.viewMatrix * self.globalTransformation * model_matrix
        self.renderSphere(mv_matrix)
    

    def renderStaticSpheres(self):
        
        #set scale
        scale = glm.scale(glm.mat4(), glm.vec3(1.5, 1.5, 1.5))
        
        # set rear sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 1.0]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.329412, 0.223529, 0.027451]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.780392, 0.568627, 0.113725]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.992157, 0.941176, 0.807843]))
        self.phongShaderProgram.setUniformMaterialShininess(27.8974)
        
        # calculate rear sphere model-view matrix
        transl = glm.translate(glm.mat4(), glm.vec3(0.0, 0.3, 1.0))
        mv_matrix = self.viewMatrix * self.globalTransformation * transl * scale
        self.renderSphere(mv_matrix)
        
        # set right sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 0.7]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.19125, 0.0735, 0.0225]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.7038, 0.27048, 0.0828]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.256777, 0.137622, 0.086014]))
        self.phongShaderProgram.setUniformMaterialShininess(12.8)
        
        # calculate right sphere model-view matrix
        transl = glm.translate(glm.mat4(), glm.vec3(0.3, 0.0, 1.0))
        mv_matrix = self.viewMatrix * self.globalTransformation * transl * scale
        self.renderSphere(mv_matrix)
        
        # set left sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 0.95]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.135, 0.2225, 0.1575]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.54, 0.89, 0.63]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.316228, 0.316228, 0.316228]))
        self.phongShaderProgram.setUniformMaterialShininess(12.8)
        
        # calculate left sphere model-view matrix
        transl = glm.translate(glm.mat4(), glm.vec3(-0.3, 0.0, 1.0))
        mv_matrix = self.viewMatrix * self.globalTransformation * transl * scale
        self.renderSphere(mv_matrix)
        
        # set front sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 0.8]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.1, 0.18725, 0.1745]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.396, 0.74151, 0.69102]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.297254, 0.30829, 0.306678]))
        self.phongShaderProgram.setUniformMaterialShininess(12.8)
        
        # calculate front sphere model-view matrix
        transl = glm.translate(glm.mat4(), glm.vec3(0.0, -0.3, 1.0))
        mv_matrix = self.viewMatrix * self.globalTransformation * transl * scale
        self.renderSphere(mv_matrix)
    

    def renderSphere(self, mv_matrix):
        
        # set MVP matrix
        self.phongShaderProgram.setUniformMVPMatrix(self.perspectiveMatrix * mv_matrix)
        
        # set model-view for ilumination calculations
        self.phongShaderProgram.setUniformModelViewMatrix(mv_matrix)
        
        self.phongSphereRenderer.render()
    
    
    def renderLight(self):
        
        #scale light source sphere matrix
        scale = glm.scale(glm.mat4(), glm.vec3(0.75, 0.75, 0.75))
        
        # translate light source sphere matrix
        transl = glm.translate(glm.mat4(), glm.vec3(self.lightPosition[0], self.lightPosition[1], self.lightPosition[2]))
                               
        # calculate model matrix and set MVP matrix
        model_matrix = self.globalTransformation * transl * scale
        self.simpleShaderProgram.setUniformMVPMatrix(self.perspectiveMatrix * self.viewMatrix * model_matrix)
        
        # set sphere color and render
        self.simpleShaderProgram.setUniformColor(np.array([1,1,1,1]))
        self.simpleSphereRenderer.render()
    
    
    def resizeGL(self, width, height):
        
        self.width = width
        self.height = height
        
        # set projection
        self.aspectRatio = int(width) / height
        self.perspectiveMatrix = glm.perspective(glm.radians(60.0), self.aspectRatio, 0.1, 50.0)

        
    def keyPressEvent(self, event):
        super(MyWidget, self).keyPressEvent(event)
        
        step = 0.2
        
        if event.key() == QtCore.Qt.Key_Up:
            self.lightPosition[1] += step
        elif event.key() == QtCore.Qt.Key_Down:
            self.lightPosition[1] -= step
        elif event.key() == QtCore.Qt.Key_Left:
            self.lightPosition[0] -= step
        elif event.key() == QtCore.Qt.Key_Right:
            self.lightPosition[0] += step
        elif event.key() == QtCore.Qt.Key_Minus:
            self.lightPosition[2] -= step
        elif event.key() == QtCore.Qt.Key_Plus:
            self.lightPosition[2] += step
        
        
        # update light source position (and light sphere position)
        self.phongShaderProgram.bind()
        light_position = glm.vec4(self.lightPosition[0], self.lightPosition[1], self.lightPosition[2], 1)
        self.phongShaderProgram.setUniformLightPosition(self.viewMatrix * self.globalTransformation * light_position)
        self.phongShaderProgram.release()
        
        self.simpleShaderProgram.bind()
        self.renderLight()
        self.simpleShaderProgram.release()
        
        
def main():
    import sys

    #Create Qt application
    app = QCoreApplication.instance()
    if app is None:
        app = QApplication(sys.argv)

    #Specify OpenGL context
    glformat = QtOpenGL.QGLFormat()
    glformat.setVersion(3, 3)
    glformat.setDoubleBuffer(True)
    glformat.setProfile(QtOpenGL.QGLFormat.CoreProfile)
    
    #Create rendering windo
    w = MyWidget(glformat)
    w.resize(1000, 500)
    w.setWindowTitle('OpenGL example')
    w.show()
    
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()<jupyter_output>TerrainMesh --> Trying to open cg/images/heightmap_1.png
opened file: size= (539, 539) format= PNG mode= L<jupyter_text>The above code, as expected, renders the following window, which can be controlled with the arrow keys:                                    This implementation was straightforward, without any difficulties to report.  Parte 2 - Cena com fonte de luz 'spotlight'Programe a cena descrita acima utilizando uma fonte de luz tipo 'spotlight' ([35_Terreno_com_esferas_iluminacao_fonte_de_luz_spotlight](35_Terreno_com_esferas_iluminacao_fonte_de_luz_spotlight.ipynb)). Neste caso, utilize também as setas 'A', 'D', 'W' e 'S' para alterar as componentes 'xy' da direção da luz. As imagens abaixo ilustram o efeito esperado ao movimentar a fonte de luz pela cena.                                      In the following code, we repeat the above scene, but setting the lighting type to *spotlight*. We include a sphere (unaffected by the ilumination) to represent our source of light and tune our attenuation parameters for the light to iluminate only close-by objects. Also, we include a key control, to control the position of our light source with the arrow and +- keys and control the direction of the *spotlight* with the A/W/S/D keys.<jupyter_code>import glm
import numpy as np
import math
import time
import OpenGL.GL as gl
from PyQt5 import QtOpenGL, QtCore
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtWidgets import QApplication

from cg.shader_programs.PhongShadingShaderProgram import PhongShadingShaderProgram
from cg.renderers.ModelRenderer_v3 import ModelRenderer
from cg.models.TerrainMesh_v1 import TerrainMesh
from cg.models.SphereMesh_v2 import SphereMesh

class MyWidget(QtOpenGL.QGLWidget):
    def initializeGL(self):
        
        # set camera pose
        self.cameraPos   = glm.vec3(0.0, 3.0, 1.5)
        self.cameraFront = glm.vec3(0.0, 0.0, 0.0)
        self.cameraUp    = glm.vec3(0.0, 1.0,  0.0)
    
        self.viewMatrix = glm.lookAt(self.cameraPos, self.cameraFront, self.cameraUp)
        
        #set initial light position and direction
        self.lightPosition = glm.vec4(0.0, 0.5, 0.0, 1)
        self.lightDirection = glm.vec3(0.2, 0.0, -1.0)
        
        
        # create spheric triangle mesh with 0.07 radius
        sphere_mesh = SphereMesh(0.07, 20, 20)
        
        # create terrain mesh size (2,2,0.5) 
        # mesh is created at the origin and paralel to the XY plane (2,2) with a depth of 0.5
        # the position of a coordinate of the terrain grid
        # can be determined wtih the getPositionAt(row, col) method
        self.terrainMesh = TerrainMesh('cg/images/heightmap_1.png', 2.0, 2.0, 0.5, 300, 300)
        
        # create objects for loading data onto GPU and rendering
        self.terrainRenderer = ModelRenderer(self.terrainMesh.getVertexPositions(),
                                             vertex_indices=self.terrainMesh.getVertexIndices(),
                                             vertex_normal=self.terrainMesh.getVertexNormals())
        
        self.phongSphereRenderer = ModelRenderer(sphere_mesh.getVertexPositions(),
                                             vertex_indices=sphere_mesh.getVertexIndices(),
                                             vertex_normal=sphere_mesh.getVertexNormals())
        
        self.simpleSphereRenderer = ModelRenderer(sphere_mesh.getVertexPositions(),
                                            vertex_indices=sphere_mesh.getVertexIndices(),
                                            vertex_normal=sphere_mesh.getVertexNormals())

        # create a phong and a simple shader
        self.phongShaderProgram = PhongShadingShaderProgram()
        self.simpleShaderProgram = SimpleShaderProgram()
        
        # set global transformation
        self.globalTransformation = glm.rotate(glm.mat4(), glm.radians(-90), glm.vec3(1.0, 0.0, 0.0))   
        
        # set phong shader properties
        self.phongShaderProgram.bind()
        self.phongShaderProgram.useUniformMaterialColor(True) 
        self.phongShaderProgram.setUniformLightMode(PhongShadingShaderProgram.SPOTLIGHT)
        self.phongShaderProgram.setUniformLightAmbient(np.array([0.2, 0.2, 0.2], dtype=np.float32))
        self.phongShaderProgram.setUniformLightIntensity(np.array([1.0, 1.0, 1.0], dtype=np.float32))
        self.phongShaderProgram.setUniformLightPosition(self.viewMatrix * self.lightPosition) # light position must be in the vision space
        self.phongShaderProgram.setUniformLightDirection(glm.mat3(self.viewMatrix * self.globalTransformation) * self.lightDirection)
        self.phongShaderProgram.release()

        self.simpleShaderProgram.bind()
        self.simpleShaderProgram.useUniformColor(True)
        self.simpleShaderProgram.release()
        
        # set model data as inputs for the phong shader
        position_loc = self.phongShaderProgram.getVertexPositionLoc()
        normal_loc = self.phongShaderProgram.getVertexNormalLoc()
        self.phongSphereRenderer.setVertexPositionLoc(position_loc)
        self.phongSphereRenderer.setVertexNormalLoc(normal_loc)
        
        # set model data as inputs for the simple shader
        position_loc = self.simpleShaderProgram.getVertexPositionLoc()
        self.simpleSphereRenderer.setVertexPositionLoc(position_loc)
        
        # configure terrain data for input
        self.terrainRenderer.setVertexPositionLoc(position_loc)
        self.terrainRenderer.setVertexNormalLoc(normal_loc)

        # enable depth testing and culling
        gl.glEnable(gl.GL_DEPTH_TEST);
        gl.glEnable(gl.GL_CULL_FACE)
        
        # initialize sphere animation variables
        # (store terrain coordinates for each sphere)
        self.terrain_index = np.array([[150, 150], [150, 150], [150, 150]], dtype=np.int32)
        self.aux_index = np.array([[150, 150], [150, 150], [150, 150]], dtype=np.int32)
        self.index_offset = np.array([[0, 0], [0, 0], [0, 0]], dtype=np.int32)
        
        # store program start time
        self.startTime = time.time()
            
       # enable blending
        gl.glEnable(gl.GL_BLEND)
        
        # configure color blend
        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        
    def paintGL(self):
        
        # set background color
        gl.glClearColor(0.3, 0.3, 0.3, 1)
        
        # clean background with the specified coolor and set depht buffer
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        
        # calculate current program time
        self.currentTime = time.time()
        time_difference = self.currentTime - self.startTime
        
        # update terrain coordinates of moving spheres
        if(time_difference > 3):
            self.startTime = self.currentTime
            time_difference = 0
            self.aux_index = self.terrain_index
            self.index_offset = np.random.randint(-100, high=100, size=(3,2))
            
        self.terrain_index = self.aux_index + (time_difference / 3.0) * self.index_offset
        self.terrain_index = np.clip(self.terrain_index, 0, 300)
        
        # activate phong shader
        self.phongShaderProgram.bind()
        
        # configure viewport
        gl.glViewport(0, 0, int(self.width), self.height)
        
        # render scene with phong and simple shader
        self.phongShaderProgram.setUniformLightDirection(glm.mat3(self.viewMatrix * self.globalTransformation) * self.lightDirection)
        self.phongShaderProgram.useUniformLightAttenuation(True)
        self.phongShaderProgram.setUniformSpotlightExpAtt(20)
        self.renderTerrain()
        self.renderMovingSpheres()
        self.renderStaticSpheres()
        self.phongShaderProgram.release()
        
        self.simpleShaderProgram.bind()
        self.renderLight()
        self.simpleShaderProgram.release()
        
        
        # re-call paintGL method
        self.update()
        
        
    def renderTerrain(self):
        
        # calculate terrain model view matrix
        self.phongShaderProgram.setUniformMVPMatrix(self.perspectiveMatrix * self.viewMatrix * self.globalTransformation)
        self.phongShaderProgram.setUniformModelViewMatrix(self.viewMatrix * self.globalTransformation)
        
        # set terrain properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([0.5, 0.25, 0.0, 1.0]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.8, 0.8, 0.8]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.8, 0.8, 0.8]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.1, 0.1, 0.1]))
        self.terrainRenderer.render()
    
 
    def renderMovingSpheres(self):
        
        # set gray sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 1.0]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.23125, 0.23125, 0.23125]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.2775, 0.2775, 0.2775]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.773911, 0.773911, 0.773911]))
        self.phongShaderProgram.setUniformMaterialShininess(89.6)
        
        # calculate gray sphere model view matrix
        sphere_position = self.terrainMesh.getPositionAt(int(self.terrain_index[0, 0]), int(self.terrain_index[0, 1]))
        model_matrix = glm.translate(glm.mat4(), glm.vec3(sphere_position[0], sphere_position[1], sphere_position[2] + 0.07))
        mv_matrix = self.viewMatrix * self.globalTransformation * model_matrix
        self.renderSphere(mv_matrix)
        
        # set red sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 1.0]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.1745, 0.01175, 0.01175]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.61424, 0.04136, 0.04136]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.727811, 0.626959, 0.626959]))
        self.phongShaderProgram.setUniformMaterialShininess(76.8)
        
        # calculate red sphere model view matrix
        sphere_position = self.terrainMesh.getPositionAt(int(self.terrain_index[1, 0]), int(self.terrain_index[1, 1]))
        model_matrix = glm.translate(glm.mat4(), glm.vec3(sphere_position[0], sphere_position[1], sphere_position[2] + 0.07))
        mv_matrix = self.viewMatrix * self.globalTransformation * model_matrix
        self.renderSphere(mv_matrix)
        
        # set orange sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 1.0]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.2125, 0.1275, 0.054]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.714, 0.4284, 0.18144]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.393548, 0.271906, 0.166721]))
        self.phongShaderProgram.setUniformMaterialShininess(25.6)
        
        # calculate orange sphere model view matrix
        sphere_position = self.terrainMesh.getPositionAt(int(self.terrain_index[2, 0]), int(self.terrain_index[2, 1]))
        model_matrix = glm.translate(glm.mat4(), glm.vec3(sphere_position[0], sphere_position[1], sphere_position[2] + 0.07))
        mv_matrix = self.viewMatrix * self.globalTransformation * model_matrix
        self.renderSphere(mv_matrix)
    
  
    def renderStaticSpheres(self):
        
        # set static sphere scale matrix
        scale = glm.scale(glm.mat4(), glm.vec3(1.5, 1.5, 1.5))
        
        # set rear sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 1.0]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.329412, 0.223529, 0.027451]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.780392, 0.568627, 0.113725]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.992157, 0.941176, 0.807843]))
        self.phongShaderProgram.setUniformMaterialShininess(27.8974)
        
        # calculate rear sphere model view matrix
        transl = glm.translate(glm.mat4(), glm.vec3(0.0, 0.3, 1.0))
        mv_matrix = self.viewMatrix * self.globalTransformation * transl * scale
        self.renderSphere(mv_matrix)
        
        # set right sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 0.7]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.19125, 0.0735, 0.0225]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.7038, 0.27048, 0.0828]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.256777, 0.137622, 0.086014]))
        self.phongShaderProgram.setUniformMaterialShininess(12.8)
        
        # calculate right sphere model view matrix
        transl = glm.translate(glm.mat4(), glm.vec3(0.3, 0.0, 1.0))
        mv_matrix = self.viewMatrix * self.globalTransformation * transl * scale
        self.renderSphere(mv_matrix)
        
        # set left sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 0.95]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.135, 0.2225, 0.1575]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.54, 0.89, 0.63]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.316228, 0.316228, 0.316228]))
        self.phongShaderProgram.setUniformMaterialShininess(12.8)
        
        # calculate left sphere model view matrix
        transl = glm.translate(glm.mat4(), glm.vec3(-0.3, 0.0, 1.0))
        mv_matrix = self.viewMatrix * self.globalTransformation * transl * scale
        self.renderSphere(mv_matrix)
        
        # set front sphere properties
        self.phongShaderProgram.setUniformMaterialColor(np.array([1.0, 1.0, 1.0, 0.8]))
        self.phongShaderProgram.setUniformMaterialAmbient(np.array([0.1, 0.18725, 0.1745]))
        self.phongShaderProgram.setUniformMaterialDiffuse(np.array([0.396, 0.74151, 0.69102]))
        self.phongShaderProgram.setUniformMaterialSpecular(np.array([0.297254, 0.30829, 0.306678]))
        self.phongShaderProgram.setUniformMaterialShininess(12.8)
        
        # calcuate front sphere model view matrix
        transl = glm.translate(glm.mat4(), glm.vec3(0.0, -0.3, 1.0))
        mv_matrix = self.viewMatrix * self.globalTransformation * transl * scale
        self.renderSphere(mv_matrix)
    

    def renderSphere(self, mv_matrix):
        
        # set MVP matrix
        self.phongShaderProgram.setUniformMVPMatrix(self.perspectiveMatrix * mv_matrix)
        
        # set model view matrix
        self.phongShaderProgram.setUniformModelViewMatrix(mv_matrix)
        self.phongSphereRenderer.render()

        
    def renderLight(self):
        
        #set scale matrix
        scale = glm.scale(glm.mat4(), glm.vec3(0.75, 0.75, 0.75))
        
        # set translation matrix
        transl = glm.translate(glm.mat4(), glm.vec3(self.lightPosition[0], self.lightPosition[1], self.lightPosition[2]))
                               
        # calculate model matrix and MVP matrix
        model_matrix = self.globalTransformation * transl * scale
        self.simpleShaderProgram.setUniformMVPMatrix(self.perspectiveMatrix * self.viewMatrix * model_matrix)
        
        # set light sphere color and render
        self.simpleShaderProgram.setUniformColor(np.array([1,1,1,1]))
        self.simpleSphereRenderer.render()
 

    def resizeGL(self, width, height):
        
        self.width = width
        self.height = height
        
        # set projection
        self.aspectRatio = int(width) / height
        self.perspectiveMatrix = glm.perspective(glm.radians(60.0), self.aspectRatio, 0.1, 50.0)

        
    def keyPressEvent(self, event):
        
        super(MyWidget, self).keyPressEvent(event)
        step = 0.2
        
        if event.key() == QtCore.Qt.Key_Up:
            self.lightPosition[1] += step
        elif event.key() == QtCore.Qt.Key_Down:
            self.lightPosition[1] -= step
        elif event.key() == QtCore.Qt.Key_Left:
            self.lightPosition[0] -= step
        elif event.key() == QtCore.Qt.Key_Right:
            self.lightPosition[0] += step
        elif event.key() == QtCore.Qt.Key_Minus:
            self.lightPosition[2] -= step
        elif event.key() == QtCore.Qt.Key_Plus:
            self.lightPosition[2] += step
        elif event.key() == QtCore.Qt.Key_W:
            self.lightDirection[1] += step
        elif event.key() == QtCore.Qt.Key_S:
            self.lightDirection[1] -= step   
        elif event.key() == QtCore.Qt.Key_D:
            self.lightDirection[0] += step 
        elif event.key() == QtCore.Qt.Key_A:
            self.lightDirection[0] -= step
        
         
        #update light position and(or) direction
        self.phongShaderProgram.bind()
        light_position = glm.vec4(self.lightPosition[0], self.lightPosition[1], self.lightPosition[2], 1)
        light_direction = glm.vec4(self.lightDirection[0], self.lightDirection[1], self.lightDirection[2],1)
        self.phongShaderProgram.setUniformLightPosition(self.viewMatrix * self.globalTransformation * light_position)
        self.phongShaderProgram.setUniformLightDirection(glm.mat3(self.viewMatrix * self.globalTransformation) * light_direction)
        self.phongShaderProgram.release()
        self.simpleShaderProgram.bind()
        self.renderLight()
        self.simpleShaderProgram.release()
        
        
def main():
    import sys

    # Create Qt application
    app = QCoreApplication.instance()
    if app is None:
        app = QApplication(sys.argv)

    # Specify openGL context
    glformat = QtOpenGL.QGLFormat()
    glformat.setVersion(3, 3)
    glformat.setDoubleBuffer(True)
    glformat.setProfile(QtOpenGL.QGLFormat.CoreProfile)
    
    # create rendering window
    w = MyWidget(glformat)
    w.resize(1000, 500)
    w.setWindowTitle('OpenGL example')
    w.show()
    
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()<jupyter_output>TerrainMesh --> Trying to open cg/images/heightmap_1.png
opened file: size= (539, 539) format= PNG mode= L
{'fraction_non_alphanumeric': 0.09710766225309311, 'fraction_numerical': 0.04281528109398741, 'mean_word_length': 2.775945087593484, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 32, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '599823', 'license': 'MIT', 'n_tokens_mistral': 12112, 'n_tokens_neox': 11138, 'n_words': 2224, 'path': '1S2020/EA979A_Ex09_AndreMedeiros.ipynb', 'repo_name': 'andre91998/EA979', 'size': '36856'}