前言
之前有一篇使用OpenGL
实现金字塔效果的文章。本篇继续使用swift
+GLSL
实现这一效果,并为金字塔加上图片与颜色混合的纹理效果。
代码
swift代码
代码如下所示,注释详细,其中的问题点解决方案都加了注释链接:
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代码的时候,我之前的代码是这么写的:
let content = try? String(contentsOfFile: file, encoding: String.Encoding.utf8)
var source = UnsafePointer<GLchar>(content)
会报错
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封装的一份代码.
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 混合。
限定符
GLSL
中的一些限定符参考如下:
效果
最终效果如下: