swift + GLSL 紋理金字塔

前言

以前有一篇使用OpenGL實現金字塔效果的文章。本篇繼續使用swift+GLSL實現這一效果,併爲金字塔加上圖片與顏色混合的紋理效果。git

代碼

swift代碼

代碼以下所示,註釋詳細,其中的問題點解決方案都加了註釋連接:github

import UIKit
import OpenGLES
import CoreGraphics

class DowImageView: UIView {
    
    private var mEaglLayer: CAEAGLLayer?
    private var mContext: EAGLContext?
    //一些id標記
    private var mColorRenderBuffer = GLuint()
    private var mColorFrameBuffer = GLuint()
    private var mprograme = GLuint()
    
    //旋轉角度
    public var xDegree: Float = 0.0 {
        didSet {
            self.renderLayer()
        }
    }
    public var yDegree: Float = 0.0
    {
        didSet {
            self.renderLayer()
        }
    }
    public var zDegree: Float = 0.0{
        didSet {
            self.renderLayer()
        }
    }
    
    //How do you override layerClass in swift: https://stackoverflow.com/questions/24351102/how-do-you-override-layerclass-in-swift
    override class var layerClass: AnyClass {
        get {
            return CAEAGLLayer.self
        }
    }
    
    /**繪圖流程
     1.建立圖層
     2.建立上下文
     3.清空緩存區
     4.設置RenderBuffer
     5.設置FrameBuffer
     6.開始繪製
     */
    override func layoutSubviews() {
        setupLayer()
        setupContext()
        deleteRenderAndFrameBuffer()
        setupRenderBuffer()
        setupFrameBuffer()
        renderLayer()
    }
    
    private func setupLayer() {
        mEaglLayer = self.layer as? CAEAGLLayer
        mEaglLayer?.isOpaque = true
        self.contentScaleFactor = UIScreen.main.scale
        //kEAGLDrawablePropertyRetainedBacking:繪圖表面顯示後,是否保留其內容
        //kEAGLColorFormatRGBA8:32位RGBA的顏色,4*8=32位
        //kEAGLColorFormatRGB565:16位RGB的顏色,
        //kEAGLColorFormatSRGBA8:sRGB表明了標準的紅、綠、藍,即CRT顯示器、LCD顯示器、投影機、打印機以及其餘設備中色彩再現所使用的三個基本色素。sRGB的色彩空間基於獨立的色彩座標,可使色彩在不一樣的設備使用傳輸中對應於同一個色彩座標體系,而不受這些設備各自具備的不一樣色彩座標的影響。
        mEaglLayer?.drawableProperties = [kEAGLDrawablePropertyRetainedBacking: false,
                                          kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8]
    }
    
    private func setupContext() {
        let context = EAGLContext(api: EAGLRenderingAPI.openGLES2)
        EAGLContext.setCurrent(context)
        mContext = context
    }
    
    //清空緩存區
    private func deleteRenderAndFrameBuffer() {
        /*
         buffer分爲frame buffer 和 render buffer2個大類。
         其中frame buffer 至關於render buffer的管理者。
         frame buffer object即稱FBO。
         render buffer則又可分爲3類。colorBuffer、depthBuffer、stencilBuffer。
         */
        
        glDeleteBuffers(1, &mColorRenderBuffer)
        mColorRenderBuffer = 0
        glDeleteBuffers(1, &mColorFrameBuffer)
        mColorFrameBuffer = 0
    }
    
    private func setupRenderBuffer() {
        //定義一個緩存區id
        var buffer = GLuint()
        //申請緩存區id
        glGenRenderbuffers(1, &buffer)
        mColorRenderBuffer = buffer
        
        //將id綁定到GL_RENDERBUFFER
        glBindRenderbuffer(GLenum(GL_RENDERBUFFER), mColorRenderBuffer)
        //綁定renderBuffer併爲其分配存儲空間
        //https://developer.apple.com/documentation/opengles/eaglcontext/1622262-renderbufferstorage
        mContext?.renderbufferStorage(Int(GL_RENDERBUFFER), from: mEaglLayer)
    }
    
    private func setupFrameBuffer() {
        var buffer = GLuint()
        glGenFramebuffers(1, &buffer)
        mColorFrameBuffer = buffer
        glBindFramebuffer(GLenum(GL_FRAMEBUFFER), mColorFrameBuffer)
        //生成幀緩存區以後,須要將renderbuffer跟framebuffer進行綁定,framebuffer用於管理renderbuffer
        glFramebufferRenderbuffer(GLenum(GL_FRAMEBUFFER), GLenum(GL_COLOR_ATTACHMENT0), GLenum(GL_RENDERBUFFER), mColorRenderBuffer)
        
    }
    
    private func renderLayer() {
        glClearColor(0.9, 0.8, 0.5, 1.0)
        glClear(GLbitfield(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))
        glEnable(GLenum(GL_DEPTH_TEST))
        
        let scale = UIScreen.main.scale
        let frame = self.frame
        //設置視口
        glViewport(0, 0, GLsizei(frame.size.width * scale), GLsizei(frame.size.height * scale))
        
        //讀取頂點、片元着色程序
        let verFile = Bundle.main.path(forResource: "shaderv", ofType: "vsh")
        let fragFile = Bundle.main.path(forResource: "shaderf", ofType: "fsh")
        
        if (mprograme != 0) {
            glDeleteProgram(mprograme)
            mprograme = 0
        }
        //將着色程序綁定到program
        attachToProgram(with: verFile, fragFIle: fragFile)
        
        //連接
        glLinkProgram(mprograme)
        //獲取連接狀態
        var linkStatus = GLint()
        glGetProgramiv(mprograme, GLenum(GL_LINK_STATUS), &linkStatus)
        
        if linkStatus == GL_FALSE {
            var message = [GLchar]()
            glGetProgramInfoLog(mprograme, GLsizei(MemoryLayout<GLchar>.size * 512), nil, &message)
            let errorInfo = String(cString: message, encoding: .utf8)
            print("programErrorInfo" + (errorInfo ?? ""))
            return
        }
        print("🍺🍻 link success")
        
        glUseProgram(mprograme)
        
        //頂點 & 顏色 & 紋理
        let attrArr: [GLfloat] = [
            -0.5, 0.5, 0.0,      0.0, 0.2, 0.3,       0.0, 1.0,//左上
            0.5, 0.5, 0.0,       0.4, 0.5, 0.0,       1.0, 1.0,//右上
            -0.5, -0.5, 0.0,     0.5, 0.9, 1.0,       0.0, 0.0,//左下
            0.5, -0.5, 0.0,      0.0, 0.4, 0.5,       1.0, 0.0,//右下
            
            0.0, 0.0, 1.0,       1.0, 0.0, 1.0,       0.5, 0.5,//頂點
            ]
        
        //索引
        let indices: [GLuint] = [
            0, 3, 2,
            0, 1, 3,
            0, 2, 4,
            0, 4, 1,
            2, 3, 4,
            1, 4, 3,
        ]
        
        //頂點數據處理
        var attrBuffer = GLuint()
        glGenBuffers(1, &attrBuffer)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), attrBuffer)
        //由內存copy到顯存
        glBufferData(GLenum(GL_ARRAY_BUFFER), MemoryLayout<GLfloat>.size * 40, attrArr, GLenum(GL_DYNAMIC_DRAW))
        
        //將頂點數據經過mPrograme傳遞到頂點着色程序的position
        
        //用來獲取vertex attribute的入口.第二參數字符串必須和shaderv.vsh中的輸入變量:position保持一致
        let position = glGetAttribLocation(mprograme, "position")
        //設置合適的格式從buffer裏面讀取數據
        glEnableVertexAttribArray(GLuint(position))
        //設置讀取方式
        //arg1:index,頂點數據的索引
        //arg2:size,每一個頂點屬性的組件數量,1,2,3,或者4.默認初始值是4.
        //arg3:type,數據中的每一個組件的類型,經常使用的有GL_FLOAT,GL_BYTE,GL_SHORT。默認初始值爲GL_FLOAT
        //arg4:normalized,固定點數據值是否應該歸一化,或者直接轉換爲固定值。(GL_FALSE)
        //arg5:stride,連續頂點屬性之間的偏移量,默認爲0;
        //arg6:指定一個指針,指向數組中的第一個頂點屬性的第一個組件。默認爲0
        glVertexAttribPointer(GLuint(position), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.size * 8), nil)
        
        //頂點顏色
        let positionColor = glGetAttribLocation(mprograme, "positionColor")
        glEnableVertexAttribArray(GLuint(positionColor))
        glVertexAttribPointer(GLuint(positionColor), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.size * 8), BUFFER_OFFSET(MemoryLayout<GLfloat>.size * 3))
        
        //處理紋理數據
        let textCoor = glGetAttribLocation(mprograme, "textCoordinate")
        glEnableVertexAttribArray(GLuint(textCoor))
        //此處bufferoffset取值應注意:https://stackoverflow.com/questions/56535272/whats-wrong-when-i-custom-an-imageview-by-opengles
        glVertexAttribPointer(GLuint(textCoor), 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(MemoryLayout<GLfloat>.size * 8), BUFFER_OFFSET(MemoryLayout<GLfloat>.size * 6))
        
        loadTexture(with: "yang")
        
        //設置紋理採樣器 0張紋理
        glUniform1i(glGetUniformLocation(mprograme, "colorMap"), 0)
        
        //獲取mProgram中的2個矩陣地址,找到f返回地址,沒有返回-1
        let projectionMatrixSlot = glGetUniformLocation(mprograme, "projectionMatrix")
        let modelViewMatrixSlot = glGetUniformLocation(mprograme, "modelViewMatrix")
        
        let width = self.frame.size.width
        let height = self.frame.size.height
        let aspect = width / height
        //建立一個單元矩陣
        var projectionMatrix: KSMatrix4 = KSMatrix4()
        ksMatrixLoadIdentity(&projectionMatrix)
        //獲取透視矩陣
        /*
         arg1:矩陣
         arg2:視角,度數爲單位
         arg3:縱橫比
         arg4:近平面距離
         arg5:遠平面距離
         */
        ksPerspective(&projectionMatrix, 10.0, Float(aspect), 5.0, 20.0)
        
        //將投影矩陣傳遞到頂點着色器
        /*
         參數列表:
         location:指要更改的uniform變量的位置
         count:更改矩陣的個數
         transpose:是否要轉置矩陣,並將它做爲uniform變量的值。必須爲GL_FALSE
         value:執行count個元素的指針,用來更新指定uniform變量
         */
        glUniformMatrix4fv(projectionMatrixSlot, GLsizei(1), GLboolean(GL_FALSE), &projectionMatrix.m.0.0)
        
        //模型視圖矩陣
        var modelViewMatrix: KSMatrix4 = KSMatrix4()
        ksMatrixLoadIdentity(&modelViewMatrix)
        ksTranslate(&modelViewMatrix, 0.0, 0.0, -10.0)
        
        //旋轉矩陣
        var rotationMatrix: KSMatrix4 = KSMatrix4()
        ksMatrixLoadIdentity(&rotationMatrix)
        //旋轉
        ksRotate(&rotationMatrix, xDegree, 1.0, 0.0, 0.0)
        ksRotate(&rotationMatrix, yDegree, 0.0, 1.0, 0.0)
        ksRotate(&rotationMatrix, zDegree, 0.0, 0.0, 1.0)
        
        var tmpModelViewMatrix = modelViewMatrix
        ksMatrixMultiply(&modelViewMatrix, &rotationMatrix, &tmpModelViewMatrix)
        
        //將模型視圖矩陣傳遞到頂點着色器
        /*
         location:指要更改的uniform變量的位置
         count:更改矩陣的個數
         transpose:是否要轉置矩陣,並將它做爲uniform變量的值。必須爲GL_FALSE
         value:執行count個元素的指針,用來更新指定uniform變量
         */
        glUniformMatrix4fv(modelViewMatrixSlot, GLsizei(1), GLboolean(GL_FALSE), &modelViewMatrix.m.0.0)
       
        //開啓正背面剔除
        glEnableClientState(GLenum(GL_CULL_FACE))

        //使用索引繪圖
        /*
         參數列表:
         mode:要呈現的畫圖的模型
         GL_POINTS
         GL_LINES
         GL_LINE_LOOP
         GL_LINE_STRIP
         GL_TRIANGLES
         GL_TRIANGLE_STRIP
         GL_TRIANGLE_FAN
         count:繪圖個數
         type:類型
         GL_BYTE
         GL_UNSIGNED_BYTE
         GL_SHORT
         GL_UNSIGNED_SHORT
         GL_INT
         GL_UNSIGNED_INT
         indices:繪製索引數組
         */
        glDrawElements(GLenum(GL_TRIANGLES), GLsizei(indices.count), GLenum(GL_UNSIGNED_INT), indices)
        
        mContext?.presentRenderbuffer(Int(GL_RENDERBUFFER))
    }
    
    private func BUFFER_OFFSET(_ i: Int) -> UnsafeRawPointer? {
        return UnsafeRawPointer(bitPattern: i)
    }
    
    //從圖片加載紋理
    private func loadTexture(with name: String) {
        
        guard let spriteImage = UIImage(named: name)?.cgImage else { return }
        let width = spriteImage.width
        let height = spriteImage.height
        //獲取圖片字節數: 寬*高*4(RGBA)
        let spriteData = calloc(width * height * 4, MemoryLayout<GLubyte>.size)
        
        //建立上下文
        //https://stackoverflow.com/questions/24109149/cgbitmapcontextcreate-error-with-swift
        /*
         arg1:data,指向要渲染的繪製圖像的內存地址
         arg2:width,bitmap的寬度,單位爲像素
         arg3:height,bitmap的高度,單位爲像素
         arg4:bitPerComponent,內存中像素的每一個組件的位數,好比32位RGBA,就設置爲8
         arg5:bytesPerRow,bitmap的沒一行的內存所佔的比特數
         arg6: 顏色空間
         arg7:colorSpace,bitmap上使用的顏色空間  kCGImageAlphaPremultipliedLast:RGBA
         */
        //bitmapInfo: https://blog.csdn.net/ccflying88/article/details/50753795
        let spriteContext = CGContext(data: spriteData, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width * 4, space: spriteImage.colorSpace!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
        
        //在CGContextRef上繪製圖片
        let rect = CGRect(x: 0, y: 0, width: width, height: height)
        spriteContext?.draw(spriteImage, in: rect)
        
        //綁定紋理到默認id, 若是隻有一個紋理取0,能夠不激活. 直接傳遞過去.
        /**.
         1. 申請緩存區標記
         2. 綁定紋理緩存區
         3. 激活紋理.
         4. 設置紋理相關參數
         */
        var texture: GLuint = GLuint()
        //n:用來生成紋理的數量
        glGenTextures(1, &texture)
        //當一個紋理被綁定時,對於其目標的GL操做將做用於該綁定的紋理之上,而且對其目標的查詢將返回該綁定紋理的狀態。
        glBindTexture(GLenum(GL_TEXTURE_2D), 0)
        glActiveTexture(GLenum((texture)))
        
        //設置紋理屬性 過濾方式 環繞方式
        glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR)
        glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR)
        glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE)
        glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE)
        
        //載入紋理數據
        /*
         arg1:紋理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
         arg2:加載的層次,通常設置爲0
         arg3:紋理的顏色值GL_RGBA
         arg4:寬
         arg5:高
         arg6:border,邊界寬度
         arg7:format
         arg8:type
         arg9:紋理數據
         */
        glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, GLsizei(width), GLsizei(height), 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), spriteData)
        free(spriteData)
        
    }
    
    private func attachToProgram(with verFile: String?, fragFIle: String?) {
        guard let verFile = verFile, let fragFIle = fragFIle else { return }
        var verShader = GLuint()
        var fragShader = GLuint()
        let program = glCreateProgram()
        compileshader(with: &verShader, type: GLenum(GL_VERTEX_SHADER), file: verFile)
        compileshader(with: &fragShader, type: GLenum(GL_FRAGMENT_SHADER), file: fragFIle)
        
        glAttachShader(program, verShader)
        glAttachShader(program, fragShader)
        
        //綁定後不須要了要釋放掉
        glDeleteShader(verShader)
        glDeleteShader(fragShader)
        
        mprograme = program
    }
    
    private func compileshader(with  shader: inout GLuint,
                               type: GLenum,
                               file: String) {
        //https://github.com/skyfe79/LearningOpenGLES2
        let content = try? String(contentsOfFile: file, encoding: String.Encoding.utf8)
        let contentCString = content?.cString(using: .utf8)
        var source = UnsafePointer<GLchar>(contentCString)
//        let content = try? NSString(contentsOfFile: file, encoding: String.Encoding.utf8.rawValue)
//        var contentLength: GLint = GLint(Int32(content!.length))
//        var contentCString = content?.utf8String

        shader = glCreateShader(type)
        
        //將着色器源碼附加到着色器對象上。
        //arg1:shader,要編譯的着色器對象
        //arg2:numOfStrings,傳遞的源碼字符串數量 1個
        //arg3:strings,着色器程序的源碼(真正的着色器程序源碼)
        //arg4:lenOfStrings,長度,具備每一個字符串長度的數組,或nil,這意味着字符串是nil終止的
        
        glShaderSource(shader, GLsizei(1),&source, nil)
//        glShaderSource(shader, GLsizei(1),&contentCString, &contentLength)
        //把着色器源代碼編譯成目標代碼
        glCompileShader(shader)
        
        var sucess = GLint()
        glGetShaderiv(shader, GLenum(GL_COMPILE_STATUS), &sucess)
        if sucess == GL_FALSE {
            var message = [GLchar]()
            glGetShaderInfoLog(shader, GLsizei(MemoryLayout<GLchar>.size * 512), nil, &message)
            let errorInfo = String(cString: message, encoding: .utf8)
            print("shaderErrorInfo:" + (errorInfo ?? ""))
        }
       
    }
}

複製代碼

其中在編譯shader代碼的時候,我以前的代碼是這麼寫的:swift

let content = try? String(contentsOfFile: file, encoding: String.Encoding.utf8)
var source = UnsafePointer<GLchar>(content)
複製代碼

會報錯api

shaderErrorInfo:ERROR: 0:15: 'premature EOF' : syntax error syntax error
shaderErrorInfo:ERROR: 0:13: 'premature EOF' : syntax error syntax error
複製代碼

根據提示premature EOF表示錯誤不是shader代碼的問題,而是shader代碼沒能被正確讀取,通過很久的查找最後發現是獲取shader代碼後須要將string轉換爲cString,上面的代碼中我給出了兩種實現方案其中一種是由String轉換爲cString,另外一種是經過NSString轉換數組

let content = try? String(contentsOfFile: file, encoding: String.Encoding.utf8)
let contentCString = content?.cString(using: .utf8)
var source = UnsafePointer<GLchar>(contentCString)
複製代碼

or緩存

let content = try? NSString(contentsOfFile: file, encoding: String.Encoding.utf8.rawValue)
var contentLength: GLint = GLint(Int32(content!.length))
var contentCString = content?.utf8String
複製代碼

頂點着色器代碼

attribute vec4 position;
attribute vec4 positionColor;
attribute vec2 textCoordinate;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
varying lowp vec2 varyTextCoord;
varying lowp vec4 varyColor;

void main() {
    varyTextCoord = textCoordinate;
    varyColor = positionColor;
    
    vec4 vPos;
    vPos = projectionMatrix * modelViewMatrix * position;
    gl_Position = vPos;
}
複製代碼

片元着色器代碼

precision highp float;
varying lowp vec2 varyTextCoord;
varying lowp vec4 varyColor;
uniform sampler2D colorMap;

void main() {
    vec4 cs = texture2D(colorMap,varyTextCoord);
    vec4 cd = varyColor;
    float s = 0.2;
    float d = 0.5;
    vec4 color = (cs * s) + (cd * d);
    gl_FragColor = color;
}
複製代碼

第三方代碼

關於矩陣的操做使用到了kesalin封裝的一份代碼.bash

GLESMath.h

//
//  GLESMath.h
//
//  Created by kesalin@gmail.com on 12-11-26.
//  Copyright (c) 2012. http://blog.csdn.net/kesalin/. All rights reserved.
//

#ifndef __GLESMATH_H__
#define __GLESMATH_H__

#import <OpenGLES/ES2/gl.h>
#include <math.h>

#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795f
#endif

#define DEG2RAD( a ) (((a) * M_PI) / 180.0f)
#define RAD2DEG( a ) (((a) * 180.f) / M_PI)

// angle indexes
#define PITCH 0 // up / down
#define YAW 1 // left / right
#define ROLL 2 // fall over

typedef unsigned char 		byte;

typedef struct
{
	GLfloat   m[3][3];
} KSMatrix3;

typedef struct
{
	GLfloat   m[4][4];
} KSMatrix4;

typedef struct KSVec3 {
    GLfloat x;
    GLfloat y;
    GLfloat z;
} KSVec3;

typedef struct KSVec4 {
    GLfloat x;
    GLfloat y;
    GLfloat z;
    GLfloat w;
} KSVec4;

typedef struct {
    GLfloat r;
    GLfloat g;
    GLfloat b;
    GLfloat a;
} KSColor;

#ifdef __cplusplus
extern "C" {
#endif

unsigned int ksNextPot(unsigned int n);
    
void ksCopyMatrix4(KSMatrix4 * target, const KSMatrix4 * src);

void ksMatrix4ToMatrix3(KSMatrix3 * target, const KSMatrix4 * src);

//
/// multiply matrix specified by result with a scaling matrix and return new matrix in result
/// result Specifies the input matrix.  Scaled matrix is returned in result.
/// sx, sy, sz Scale factors along the x, y and z axes respectively
//
void ksScale(KSMatrix4 *result, GLfloat sx, GLfloat sy, GLfloat sz);

//
/// multiply matrix specified by result with a translation matrix and return new matrix in result
/// result Specifies the input matrix.  Translated matrix is returned in result.
/// tx, ty, tz Scale factors along the x, y and z axes respectively
//
void ksTranslate(KSMatrix4 *result, GLfloat tx, GLfloat ty, GLfloat tz);

//
/// multiply matrix specified by result with a rotation matrix and return new matrix in result
/// result Specifies the input matrix.  Rotated matrix is returned in result.
/// angle Specifies the angle of rotation, in degrees.
/// x, y, z Specify the x, y and z coordinates of a vector, respectively
//
void ksRotate(KSMatrix4 *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z);

//
/// perform the following operation - result matrix = srcA matrix * srcB matrix
/// result Returns multiplied matrix
/// srcA, srcB Input matrices to be multiplied
//
void ksMatrixMultiply(KSMatrix4 *result, const KSMatrix4 *srcA, const KSMatrix4 *srcB);

//
//// return an identity matrix 
//// result returns identity matrix
//
void ksMatrixLoadIdentity(KSMatrix4 *result);

//
/// multiply matrix specified by result with a perspective matrix and return new matrix in result
/// result Specifies the input matrix.  new matrix is returned in result.
/// fovy Field of view y angle in degrees
/// aspect Aspect ratio of screen
/// nearZ Near plane distance
/// farZ Far plane distance
//
void ksPerspective(KSMatrix4 *result, float fovy, float aspect, float nearZ, float farZ);

//
/// multiply matrix specified by result with a perspective matrix and return new matrix in result
/// result Specifies the input matrix.  new matrix is returned in result.
/// left, right Coordinates for the left and right vertical clipping planes
/// bottom, top Coordinates for the bottom and top horizontal clipping planes
/// nearZ, farZ Distances to the near and far depth clipping planes.  These values are negative if plane is behind the viewer
//
void ksOrtho(KSMatrix4 *result, float left, float right, float bottom, float top, float nearZ, float farZ);

//
// multiply matrix specified by result with a perspective matrix and return new matrix in result
/// result Specifies the input matrix.  new matrix is returned in result.
/// left, right Coordinates for the left and right vertical clipping planes
/// bottom, top Coordinates for the bottom and top horizontal clipping planes
/// nearZ, farZ Distances to the near and far depth clipping planes.  Both distances must be positive.
//
void ksFrustum(KSMatrix4 *result, float left, float right, float bottom, float top, float nearZ, float farZ);

#ifdef __cplusplus
}
#endif

#endif // __GLESMATH_H__

複製代碼

GLESMath.c

//
//  GLESMath.c
//
//  Created by kesalin@gmail.com on 12-11-26.
//  Copyright (c) 2012 ƒÍ http://blog.csdn.net/kesalin/. All rights reserved.
//

#include "GLESMath.h"
#include <stdlib.h>
#include <math.h>

void * memcpy(void *, const void *, size_t);
void * memset(void *, int, size_t);

//
// Matrix math utils
//

void ksScale(KSMatrix4 *result, GLfloat sx, GLfloat sy, GLfloat sz)
{
    result->m[0][0] *= sx;
    result->m[0][1] *= sx;
    result->m[0][2] *= sx;
    result->m[0][3] *= sx;
    
    result->m[1][0] *= sy;
    result->m[1][1] *= sy;
    result->m[1][2] *= sy;
    result->m[1][3] *= sy;
    
    result->m[2][0] *= sz;
    result->m[2][1] *= sz;
    result->m[2][2] *= sz;
    result->m[2][3] *= sz;
}

void ksTranslate(KSMatrix4 *result, GLfloat tx, GLfloat ty, GLfloat tz)
{
    result->m[3][0] += (result->m[0][0] * tx + result->m[1][0] * ty + result->m[2][0] * tz);
    result->m[3][1] += (result->m[0][1] * tx + result->m[1][1] * ty + result->m[2][1] * tz);
    result->m[3][2] += (result->m[0][2] * tx + result->m[1][2] * ty + result->m[2][2] * tz);
    result->m[3][3] += (result->m[0][3] * tx + result->m[1][3] * ty + result->m[2][3] * tz);
}

void ksRotate(KSMatrix4 *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
{
    GLfloat sinAngle, cosAngle;
    GLfloat mag = sqrtf(x * x + y * y + z * z);
    
    sinAngle = sinf ( angle * M_PI / 180.0f );
    cosAngle = cosf ( angle * M_PI / 180.0f );
    if ( mag > 0.0f )
    {
        GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs;
        GLfloat oneMinusCos;
        KSMatrix4 rotMat;
        
        x /= mag;
        y /= mag;
        z /= mag;
        
        xx = x * x;
        yy = y * y;
        zz = z * z;
        xy = x * y;
        yz = y * z;
        zx = z * x;
        xs = x * sinAngle;
        ys = y * sinAngle;
        zs = z * sinAngle;
        oneMinusCos = 1.0f - cosAngle;
        
        rotMat.m[0][0] = (oneMinusCos * xx) + cosAngle;
        rotMat.m[0][1] = (oneMinusCos * xy) - zs;
        rotMat.m[0][2] = (oneMinusCos * zx) + ys;
        rotMat.m[0][3] = 0.0F; 
        
        rotMat.m[1][0] = (oneMinusCos * xy) + zs;
        rotMat.m[1][1] = (oneMinusCos * yy) + cosAngle;
        rotMat.m[1][2] = (oneMinusCos * yz) - xs;
        rotMat.m[1][3] = 0.0F;
        
        rotMat.m[2][0] = (oneMinusCos * zx) - ys;
        rotMat.m[2][1] = (oneMinusCos * yz) + xs;
        rotMat.m[2][2] = (oneMinusCos * zz) + cosAngle;
        rotMat.m[2][3] = 0.0F; 
        
        rotMat.m[3][0] = 0.0F;
        rotMat.m[3][1] = 0.0F;
        rotMat.m[3][2] = 0.0F;
        rotMat.m[3][3] = 1.0F;
        
        ksMatrixMultiply( result, &rotMat, result );
    }
}

void ksMatrixMultiply(KSMatrix4 *result, const KSMatrix4 *srcA, const KSMatrix4 *srcB)
{
    KSMatrix4    tmp;
    int         i;
    
	for (i=0; i<4; i++)
	{
		tmp.m[i][0] =	(srcA->m[i][0] * srcB->m[0][0]) +
        (srcA->m[i][1] * srcB->m[1][0]) +
        (srcA->m[i][2] * srcB->m[2][0]) +
        (srcA->m[i][3] * srcB->m[3][0]) ;
        
		tmp.m[i][1] =	(srcA->m[i][0] * srcB->m[0][1]) + 
        (srcA->m[i][1] * srcB->m[1][1]) +
        (srcA->m[i][2] * srcB->m[2][1]) +
        (srcA->m[i][3] * srcB->m[3][1]) ;
        
		tmp.m[i][2] =	(srcA->m[i][0] * srcB->m[0][2]) + 
        (srcA->m[i][1] * srcB->m[1][2]) +
        (srcA->m[i][2] * srcB->m[2][2]) +
        (srcA->m[i][3] * srcB->m[3][2]) ;
        
		tmp.m[i][3] =	(srcA->m[i][0] * srcB->m[0][3]) + 
        (srcA->m[i][1] * srcB->m[1][3]) +
        (srcA->m[i][2] * srcB->m[2][3]) +
        (srcA->m[i][3] * srcB->m[3][3]) ;
	}
    
    memcpy(result, &tmp, sizeof(KSMatrix4));
}

void ksCopyMatrix4(KSMatrix4 * target, const KSMatrix4 * src)
{
    memcpy(target, src, sizeof(KSMatrix4));
}

void ksMatrix4ToMatrix3(KSMatrix3 * t, const KSMatrix4 * src)
{
    t->m[0][0] = src->m[0][0];
    t->m[0][1] = src->m[0][1];
    t->m[0][2] = src->m[0][2];
    t->m[1][0] = src->m[1][0];
    t->m[1][1] = src->m[1][1];
    t->m[1][2] = src->m[1][2];
    t->m[2][0] = src->m[2][0];
    t->m[2][1] = src->m[2][1];
    t->m[2][2] = src->m[2][2];
}

void ksMatrixLoadIdentity(KSMatrix4 *result)
{
    memset(result, 0x0, sizeof(KSMatrix4));

    result->m[0][0] = 1.0f;
    result->m[1][1] = 1.0f;
    result->m[2][2] = 1.0f;
    result->m[3][3] = 1.0f;
}

void ksFrustum(KSMatrix4 *result, float left, float right, float bottom, float top, float nearZ, float farZ)
{
    float       deltaX = right - left;
    float       deltaY = top - bottom;
    float       deltaZ = farZ - nearZ;
    KSMatrix4    frust;
    
    if ( (nearZ <= 0.0f) || (farZ <= 0.0f) ||
        (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f) )
        return;
    
    frust.m[0][0] = 2.0f * nearZ / deltaX;
    frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
    
    frust.m[1][1] = 2.0f * nearZ / deltaY;
    frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
    
    frust.m[2][0] = (right + left) / deltaX;
    frust.m[2][1] = (top + bottom) / deltaY;
    frust.m[2][2] = -(nearZ + farZ) / deltaZ;
    frust.m[2][3] = -1.0f;
    
    frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
    frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
    
    ksMatrixMultiply(result, &frust, result);
}

void ksPerspective(KSMatrix4 *result, float fovy, float aspect, float nearZ, float farZ)
{
    GLfloat frustumW, frustumH;
    
    frustumH = tanf( fovy / 360.0f * M_PI ) * nearZ;
    frustumW = frustumH * aspect;
    
    ksFrustum( result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ );
}

void ksOrtho(KSMatrix4 *result, float left, float right, float bottom, float top, float nearZ, float farZ)
{
    float       deltaX = right - left;
    float       deltaY = top - bottom;
    float       deltaZ = farZ - nearZ;
    KSMatrix4    ortho;
    
    if ( (deltaX == 0.0f) || (deltaY == 0.0f) || (deltaZ == 0.0f) )
        return;
    
    ksMatrixLoadIdentity(&ortho);
    ortho.m[0][0] = 2.0f / deltaX;
    ortho.m[3][0] = -(right + left) / deltaX;
    ortho.m[1][1] = 2.0f / deltaY;
    ortho.m[3][1] = -(top + bottom) / deltaY;
    ortho.m[2][2] = -2.0f / deltaZ;
    ortho.m[3][2] = -(nearZ + farZ) / deltaZ;
    
    ksMatrixMultiply(result, &ortho, result);
}
複製代碼

顏色混合

在片元着色器紋理加載時使圖片顏色和自定義的顏色作了混合,混合方式參見以前寫的OpenGL 混合app

限定符

GLSL中的一些限定符參考以下:ide

效果

最終效果以下:post

相關文章
相關標籤/搜索