OpenGL ES 天空盒子 swift

2019-07-05  本文已影响0人  HChase
skybox.gif
    override func viewDidLoad() {
        super.viewDidLoad()
       
        // 1. 设置上下文
        setupContext()
        
        // 2. 设置飞机的GLKBaseEffect
        setupBaseEffect()
        
        // 3. 将顶点,法线数据从cpu 复制到 gpu
        setupBuffers()
        
        // 4. 开启深度测试和剔除   不然会绘制背部,和出现闪烁效果
        glEnable(GLenum(GL_CULL_FACE))
        glEnable(GLenum(GL_DEPTH_TEST))
        
        // 5. 加载天空盒子的天空纹理
        setupSkyBoxTexture()
        
        // 6. 设置视角
        setMatrices()
    }

设置上下文

创建EAGLContext对象,将该对象赋给GLKView对象,然后设置为当前上下文;

 func setupContext() {
        let view = self.view as! GLKView
        view.context = context!
        view.drawableColorFormat = .RGBA8888
        view.drawableDepthFormat = .format24
        EAGLContext.setCurrent(context!)
    }

设置飞机的BaseEffect

主要为了设置绘制飞机模型的GLKBaseEffect,并设置一个光照信息;

    // 设置飞机的BaseEffect
    func setupBaseEffect() {
        baseEffect.light0.enabled = GLboolean(GL_TRUE)
        baseEffect.light0.position = GLKVector4Make(0.0, 0.0, 2.0, 1.0)
        baseEffect.light0.specularColor = GLKVector4Make(0.25, 0.25, 0.25, 1.0)
        baseEffect.light0.diffuseColor = GLKVector4Make(0.75, 0.75, 0.75, 1.0)
        baseEffect.lightingType = .perPixel
    }

设置缓存,将数据从cpu 复制到 gpu

创建两个缓存,用来存放飞机模型顶点法线数组,并使用glBufferData方法,将数据从cpu 复制到gpu;

   func setupBuffers() {
        
        // 1. 设置顶点缓存
        var buffer:GLuint = 0
        glGenBuffers(1, &buffer)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), buffer)
        
        var starshipPositions1 = starshipPositions
        glBufferData(GLenum(GL_ARRAY_BUFFER), MemoryLayout.size(ofValue: starshipPositions1), &starshipPositions1 , GLenum(GL_STATIC_DRAW))
        glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue)) // 设置顶点可见
        glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.stride*0), nil) // 设置顶点数据的读取方式
        
        mPositionBuffer = buffer

        
        // 2. 设置法线缓存
        glGenBuffers(1, &buffer)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), buffer)
        
        starshipPositions1 = starshipNormals
        glBufferData(GLenum(GL_ARRAY_BUFFER), MemoryLayout.size(ofValue: starshipPositions1), &starshipPositions1 , GLenum(GL_STATIC_DRAW))
        glEnableVertexAttribArray(GLuint(GLKVertexAttrib.normal.rawValue)) // 设置法线可见
        glVertexAttribPointer(GLuint(GLKVertexAttrib.normal.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.stride*0), nil) // 设置发现数据的读取方式
        
        mNormalBuffer = buffer
       
    }

加载天空的纹理

使用苹果GLKit框架的GLKTextureLoader来加载纹理,更加方便;然后将纹理设置到天空盒子对象GLKSkyboxEffect

  func setupSkyBoxTexture() {
        
        let path = Bundle.main.path(forResource: "image", ofType: "png")!
        let url = URL.init(fileURLWithPath: path)
        
        let textureInfo = try? GLKTextureLoader.cubeMap(withContentsOf: url, options: nil)
        
        guard let textureI = textureInfo  else {
            return
        }
        
        skyboxEffect.textureCubeMap.name = textureI.name
        skyboxEffect.textureCubeMap.target = GLKTextureTarget(rawValue: textureI.target)!
        
    }

设置模型视图矩阵

由于在天空盒子中,每一帧都会改变观察者的位置,所以在准备刷新每一帧时,都将更新观察着的位置;观察者围绕着飞机做旋转,所以使用观察者位置(eyePosition)可以设置为圆形环绕;做半径为5的逆时针环绕;

image.png

等价于

 eyePosition = GLKVector3Make(5.0 * sin(angle), -5.0, 5.0 * cos(angle))  // 眼睛位置
       func setMatrices() {
        let aspectRatio: GLfloat = GLfloat(self.view.bounds.width / self.view.bounds.height)
        baseEffect.transform.projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(85.0), aspectRatio, 0.2, 23.0)
        
        baseEffect.transform.modelviewMatrix = GLKMatrix4MakeLookAt(eyePosition.x, eyePosition.y, eyePosition.z, lookAtPosition.x, lookAtPosition.y, lookAtPosition.z, upVector.x, upVector.y, upVector.z)
        
        skyboxEffect.center = self.eyePosition
        skyboxEffect.transform.projectionMatrix = baseEffect.transform.projectionMatrix
        skyboxEffect.transform.modelviewMatrix = baseEffect.transform.modelviewMatrix
        

        angle += 0.01
        
        eyePosition = GLKVector3Make(5.0 * sin(angle), -5.0, 5.0 * cos(angle))  // 眼睛位置
        lookAtPosition = GLKVector3Make(0.0 , 1.5 + -5.0 * sin(0.3 * angle), 0.0) // 观察的位置, 为了效果更好上下浮动
    }

绘制飞机和天空盒子

需要在每一帧刷新时的GLKViewDelegate代理方法中,来绘制模型;

    override func glkView(_ view: GLKView, drawIn rect: CGRect) {
        glClearColor(0.5, 0.1, 0.1, 1.0)
        glClear(GLbitfield(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))
        
        // 1. 改变视角
        setMatrices()
        
        // 2. 绘制天空盒子
        skyboxEffect.prepareToDraw()
        glDepthMask(GLboolean(GL_FALSE))
        skyboxEffect.draw()
        glDepthMask(GLboolean(GL_TRUE))
        
        
        //glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), 0)
        //glBindTexture(GLenum(GL_TEXTURE_CUBE_MAP), 0)

        // 3. 读取飞机模型数据
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), mPositionBuffer!);
        glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue));
        glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, nil);
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), mNormalBuffer!);
        glEnableVertexAttribArray(GLuint(GLKVertexAttrib.normal.rawValue));
        glVertexAttribPointer(GLuint(GLKVertexAttrib.normal.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, nil);
    
        
        // 转换数据     将元组变为数组
        let diffusesM = Mirror(reflecting: starshipDiffuses)
        let specularsM = Mirror(reflecting: starshipSpeculars)
        let firstsM = Mirror(reflecting: starshipFirsts)
        let countsM = Mirror(reflecting: starshipCounts)
        
    
        var diffusesArr:[ (GLfloat, GLfloat, GLfloat)] = []
        var specularsArr:[ (GLfloat, GLfloat, GLfloat)] = []
        var firstsArr:[ GLint] = []
        var countsArr:[ GLint] = []
        
        
        for diffuses in diffusesM.children {
            let value = diffuses.value as! (GLfloat, GLfloat, GLfloat)
            diffusesArr.append(value)
        }

        for speculars in specularsM.children {
            let value = speculars.value as! (GLfloat, GLfloat, GLfloat)
            specularsArr.append(value)
        }

        for firsts in firstsM.children {
            let value = firsts.value as! (GLint)
            firstsArr.append(value)
        }
        
        for counts in countsM.children {
            let value = counts.value as! (GLint)
            countsArr.append(value)
        }
   
        
        // 4. 绘制飞机   需要绘制不同的颜色,所以需要for循环
        for i in 0..<diffusesArr.count {

            let diffusesValue = diffusesArr[I]
            let specularsValue = specularsArr[I]
            let firstValue = firstsArr[I]
            let countValue = countsArr[I]

            self.baseEffect.material.diffuseColor = GLKVector4Make(diffusesValue.0, diffusesValue.1, diffusesValue.2, 1.0);
            self.baseEffect.material.specularColor = GLKVector4Make(specularsValue.0, specularsValue.1, specularsValue.2, 1.0);
            baseEffect.prepareToDraw()
            glDrawArrays(GLenum(GL_TRIANGLES), firstValue, countValue);
        }
        
        // 如果不想使用多种颜色的飞机,可以这样写,比较简洁
//        baseEffect.prepareToDraw()
//        glDrawArrays(GLenum(GL_TRIANGLES), 0, 30+33+3); // 30+33+3 == starshipCounts

        
    }

源代码demo

上一篇下一篇

猜你喜欢

热点阅读