源码&参考:知乎 雪流星
阴影部分的知识打算看完games202后再完善
目录
ObjFileReader类---读取模型文件,存入Mesh的Buffer中
一、在C++中创建一个窗口
为了把渲染的图片放在窗口里,不用反复打开图片查看
不重要,以下代码实现了创建并显示一个窗口的功能,入口函数是WinMain
不过本项目中是用的main为入口函数,手动写了一个Window类
#include<Windows.h>
//自定义的窗口过程
LRESULT CALLBACK MyWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR ICmdLine, int nCmdShow) {
// 创建窗口类实例wnd
WNDCLASS wnd = {
CS_HREDRAW,
MyWindowProc, // 使用自定义的窗口过程函数
0,0,hInstance,
LoadIcon(NULL,IDI_APPLICATION),
LoadCursor(NULL,IDC_ARROW),
(HBRUSH)(GetStockObject(WHITE_BRUSH)),
NULL, L"mic@Renderer"
};
// 注册窗口类
RegisterClass(&wnd);
// 创建窗口
HWND hWnd = CreateWindow(L"mic@Renderer", L"mic@Renderer", WS_OVERLAPPEDWINDOW,
100, 100, 500, 500, NULL, NULL, hInstance, NULL);
// 显示窗口
ShowWindow(hWnd, nCmdShow);
// 更新窗口
UpdateWindow(hWnd);
//消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) { // 循环获取消息
TranslateMessage(&msg); // 翻译消息
DispatchMessage(&msg); // 派发消息
}
return 0;
}
Window类---负责开窗口并实时更新
#pragma once
#include<Windows.h>
class Window {
public:
HWND window; //HWND是一个基本类型--窗口句柄(句柄是Windows系统中对象或实例的标识)
int windowWidth;
int windowHeight;
HDC hdc;
HDC screenHDC; //HDC是指窗体、控件的句柄
//构造和析构
Window(int w, int h, const char* name);
~Window();
};
#include "Window.h"
#include <iostream>
using namespace std;
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Window* window = static_cast<Window*>(GetPropA(hWnd, "Owner"));
if (!window)
return DefWindowProcA(hWnd, msg, wParam, lParam);
switch (msg)
{
}
return DefWindowProcA(hWnd, msg, wParam, lParam);
}
Window::Window(int w, int h, const char* name) :windowWidth(w), windowHeight(h)
{
WNDCLASS wndClass = { CS_BYTEALIGNCLIENT, (WNDPROC)MsgProc, 0, 0, 0, NULL, NULL, NULL, NULL, TEXT("Test") };
wndClass.hInstance = GetModuleHandle(NULL);
if (!RegisterClass(&wndClass))
return;
window = CreateWindow(TEXT("Test"), TEXT("Test"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
0, 0, 0, 0, NULL, NULL, wndClass.hInstance, NULL);
if (window == NULL)
return;
hdc = GetDC((window));
screenHDC = CreateCompatibleDC(hdc);
//ReleaseDC(handler, hdc);
BITMAPINFO bitmapInfo = { { sizeof(BITMAPINFOHEADER),windowWidth, windowHeight, 1, 32, BI_RGB, windowWidth * windowHeight * 4, 0, 0, 0, 0 } };
LPVOID ptr;
//创建设备无关的位图
HBITMAP bitmapHandler = CreateDIBSection(screenHDC, &bitmapInfo, DIB_RGB_COLORS, &ptr, 0, 0);
if (bitmapHandler == NULL)
return;
HBITMAP screenObject = (HBITMAP)SelectObject(screenHDC, bitmapHandler);
SetWindowPos(window, NULL, 50, 50, windowWidth, windowHeight, (SWP_NOCOPYBITS | SWP_NOZORDER | SWP_SHOWWINDOW));
ShowWindow(window, SW_NORMAL);
UpdateWindow(window);
}
Window::~Window()
{
}
二、需要的头文件
Color类---定义了颜色信息
有float r,g,b,a四个属性
重载了加减乘除运算符实现颜色间的运算
定义了颜色的插值函数
#pragma once
class Color {
float r, g, b, a;
Color(float cr, float cg, float cb, float ca);
Color() {};
~Color();
//操作符重载---Color+Color
Color operator+(const Color& right)const;
//操作符重载---Color+常数
Color operator+(const float c) {
return Color(r + c, g + c, b + c, a);
}
//操作符重载---Color-Color
Color operator-(const Color& right)const;
//操作符重载---Color*Color
Color operator*(const Color& right)const;
//操作符重载---Color*常数
Color operator*(float value)const;
//操作符重载---Color/Color
Color operator/(float value)const;
//Color线性插值
static Color Lerp(const Color& left, const Color& right, float t);
//白色
static Color white;
};
#include "Color.h"
Color::Color(float cr, float cg, float cb, float ca):r(cr),g(cg),b(cb),a(ca)
{
}
Color::~Color()
{
}
Color Color::operator+(const Color& right) const
{
Color returnValue(r + right.r, g + right.g, b + right.b, a + right.a);
return returnValue;
}
Color Color::operator-(const Color& right) const
{
Color returnValue(r - right.r, g - right.g, b - right.b, a - right.a);
return returnValue;
}
Color Color::operator*(const Color& right) const
{
Color returnValue(r * right.r, g * right.g, b * right.b, a * right.a);
return returnValue;
}
Color Color::operator*(float value) const
{
Color returnValue(r * value, g * value, b * value, a * value);
return returnValue;
}
Color Color::operator/(float value) const
{
float rhvalue = 1.0f / value; //倒数,变除法为乘法
Color returnValue(r * rhvalue, g * rhvalue, b * rhvalue, a * rhvalue);
return returnValue;
}
Color Color::Lerp(const Color& left, const Color& right, float t)
{
return left + (right - left) * t;
}
Color Color::white = Color(1, 1, 1, 1);
Vector3类---三维向量
注意:类模板的声明和定义应该放在一个文件内
有float x,y,z,w四个属性
定义了点乘(对应位相乘,结果是数),叉乘(“求谁盖谁,+ - +”,结果是向量),求模等常见操作,定义了单位化(模长为1)和标准化(w为1)函数
此文件中顺便定义了Vector2类,有 float x y两个属性
#pragma once
template<class T>
class Vector3 {
public:
T x, y, z, w;
Vector3<T>(T fx, T fy, T fz);
Vector3<T>(T fx, T fy, T fz, T fw);
//默认构造参数(0,0,0,1)
Vector3<T>() {
x = y = z = 0;
w = 1;
}
//向量+向量
Vector3<T> operator+(const Vector3<T>& right)const;
//向量-向量
Vector3<T> operator-(const Vector3<T>& right)const;
//向量*常数
Vector3<T>operator*(float value)const;
//向量/常数
Vector3<T>operator/(float value)const;
//求模长^2
float SquareMagnitude();
//求模长
float Magnitude();
//单位化
Vector3<T>& Normalize();
//点乘
float static Dot(const Vector3<T>& left, const Vector3<T>& right);
float Dot(const Vector3<T>& right);
//叉乘
Vector3<T> static Cross(const Vector3<T>& left, const Vector3<T>& right);
//插值
Vector3<T> static Lerp(const Vector3<T>& left, const Vector3<T>& right, float t);
//标准化(w变成1)
void standardization()
{
if (w == 0)
{
std::cout << "error w==0" << std::endl;
return;
}
x /= w;
y /= w;
z /= w;
w = 1;
}
void Print();
};
using Vector3f = Vector3<float>;
using Vector3i = Vector3<int>;
template<class T>
Vector3<T>::Vector3(T fx, T fy, T fz) {
x = fx;
y = fy;
z = fz;
w = 1;
}
template<class T>
Vector3<T> Vector3<T>::operator+(const Vector3<T>& right)const
{
Vector3<T> returnValue(x + right.x, y + right.y, z + right.z);
return returnValue;
}
template<class T>
Vector3<T> Vector3<T>::operator-(const Vector3<T>& right)const
{
Vector3<T> returnValue(x - right.x, y - right.y, z - right.z);
return returnValue;
}
template<class T>
Vector3<T> Vector3<T>::operator*(float value)const
{
Vector3<T> returnValue(x * value, y * value, z * value);
return returnValue;
}
template<class T>
Vector3<T> Vector3<T>::operator/(float value)const
{
Vector3<T> returnValue(x / value, y / value, z / value);
return returnValue;
}
template<class T>
float Vector3<T>::SquareMagnitude() {
return Dot(*this, *this);
}
template<class T>
float Vector3<T>::Magnitude() {
return sqrt(SquareMagnitude());
}
template<class T>
Vector3<T>& Vector3<T>::Normalize() {
*this = *this / Magnitude();
return *this;
}
//点乘返回一个值---对应位相乘后求和(没涉及w)
template<class T>
float Vector3<T>::Dot(const Vector3<T>& left, const Vector3<T>& right)
{
return left.x * right.x + left.y * right.y + left.z * right.z;
}
template<class T>
float Vector3<T>::Dot(const Vector3<T>& right)
{
return x * right.x + y * right.y + z * right.z;
}
//叉乘
template<class T>
Vector3<T> Vector3<T>::Cross(const Vector3<T>& left, const Vector3<T>& right) {
float valueX = left.y * right.z - left.z * right.y;
float valueY = left.z * right.x - left.x * right.z;
float valueZ = left.x * right.y - left.y * right.x;
Vector3<T> returnValue(valueX, valueY, valueZ);
return returnValue;
}
template<class T>
Vector3<T> Vector3<T>::Lerp(const Vector3<T>& left, const Vector3<T>& right,float t) {
return left + (right - left) * t;
}
template<class T>
void Vector3<T>::Print() {
std::cout << "Vector3<T> x: " << x << " y: " << y << " z: " << z << std::endl;
}
//同时定义了二维向量
class Vector2 {
public:
float x, y;
Vector2() { x = y = 0; }
Vector2(float fx,float fy) {
x = fx;
y = fy;
}
~Vector2() {};
Vector2 operator + (const Vector2& right) const
{
return Vector2(x + right.x, y + right.y);
}
Vector2 operator - (const Vector2& right) const
{
return Vector2(x - right.x, y - right.y);
}
Vector2 operator * (float value) const
{
return Vector2(x * value, y * value);
}
Vector2 operator / (float value) const
{
return Vector2(x / value, y / value);
}
};
Matrix类---4x4矩阵
成员变量是4*4的二维数组
定义了矩阵的运算,矩阵与向量的运算等
(特别记忆:矩阵的乘法:m.value[i][j] += this->value[i][k] *right.value[k][j];)
#pragma once
#include"Vector3.h"
class Matrix {
public:
float value[4][4]; //4x4的矩阵
Matrix();
~Matrix();
Matrix operator+(const Matrix& right)const;
Matrix operator-(const Matrix& right)const;
Matrix operator*(const Matrix& right)const;
Matrix operator*(float k)const;
//矩阵x向量
Vector3f MutiplyVector3(const Vector3f& v)const;
//正交化矩阵
void Identity();
//矩阵转置
Matrix transpose()const {
Matrix trans;
for(int i=0;i<4;i++)
for (int j = 0; j < 4; j++) {
trans.value[i][j] = value[j][i];
}
return trans;
}
//打印
void Print();
};
#include "Matrix.h"
#include <iostream>
Matrix::Matrix()
{
Identity();
}
Matrix::~Matrix()
{
}
Matrix Matrix::operator+(const Matrix& right) const
{
Matrix m;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
m.value[i][j] = this->value[i][j] + right.value[i][j];
}
}
return m;
}
Matrix Matrix::operator-(const Matrix& right) const
{
Matrix m;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
m.value[i][j] = this->value[i][j] - right.value[i][j];
}
}
return m;
}
Matrix Matrix::operator*(const Matrix& right) const
{
Matrix m;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 4; k++) {
m.value[i][j] += this->value[i][k] *right.value[k][j];
}
}
}
return m;
}
Matrix Matrix::operator*(float k) const
{
Matrix m;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
m.value[i][j] = this->value[i][j] * k;
}
}
return m;
}
Vector3f Matrix::MutiplyVector3(const Vector3f& v) const
{
float x = v.x * value[0][0] + v.y * value[0][1] + v.z * value[0][2] + v.w * value[0][3];
float y = v.x * value[1][0] + v.y * value[1][1] + v.z * value[1][2] + v.w * value[1][3];
float z = v.x * value[2][0] + v.y * value[2][1] + v.z * value[2][2] + v.w * value[2][3];
float w = v.x * value[3][0] + v.y * value[3][1] + v.z * value[3][2] + v.w * value[3][3];
Vector3f returnValue(x, y, z);
returnValue.w = w;
return returnValue;
}
void Matrix::Identity()
{
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (i == j)
value[i][j] = 1;
else
value[i][j] = 0;
}
}
}
void Matrix::Print()
{
std::cout << "-----------------Matrix Begin--------------" << std::endl;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
std::cout << "[" << value[i][j] << "] ";
}
std::cout << std::endl;
}
std::cout << "-----------------Matrix End----------------" << std::endl;
}
Vertex类---模型的顶点
顶点信息记录了:Vector3f 顶点坐标、Vector3f 法线方向、颜色信息,Vector2 uv坐标
定义了顶点信息插值(深度插值 颜色插值 uv坐标插值)、顶点与矩阵相乘等方法(只有pos和normal成员需要乘上矩阵)
#pragma once
#include"Color.h"
#include"Matrix.h"
class Vertex {
public:
Vector3f pos; //顶点坐标
Vector3f normal;//法线向量
Color color; //颜色信息
Vector2 uv; //uv坐标
Vertex() {};
Vertex(const Vector3f& p, const Color& c, Vector2 uv);
Vertex(const Vector3f& p, const Color& c, const Vector3f& normal, Vector2 uv);
~Vertex();
//对顶点信息进行插值
void LerpVertexData(Vertex& left, const Vertex& right, float t);
//打印顶点坐标和RGB颜色信息
void Print();
//顶点*矩阵
Vertex& operator*(const Matrix& m);
static float LerpFloat(float v1, float v2, float t) { return v1 + (v2 - v1) * t; }
};
#include"Vertex.h"
#include"Vector3.h"
#include <iostream>
Vertex::Vertex(const Vector3f& p, const Color& c, Vector2 uv)
: pos(p), color(c), uv(uv)
{
}
Vertex::Vertex(const Vector3f& p, const Color& c, const Vector3f& normal, Vector2 uv) : pos(p), color(c), normal(normal), uv(uv)
{
}
Vertex::~Vertex()
{
}
void Vertex::LerpVertexData(Vertex& left, const Vertex& right, float t)
{
pos.z = LerpFloat(left.pos.z, right.pos.z, t); //深度插值
color = Color::Lerp(left.color, right.color, t);//颜色信息插值
uv.x = LerpFloat(left.uv.x, right.uv.x, t); //uv坐标插值
uv.y = LerpFloat(left.uv.y, right.uv.y, t);
}
void Vertex::Print()
{
std::cout << "Vector3f: " << pos.x << " " << pos.y << " " << pos.z;
std::cout << " Color: " << color.r << " " << color.g << " " << color.b << std::endl;
}
Vertex& Vertex::operator*(const Matrix& m)
{
//顶点乘一个变换矩阵时,只有顶点和法线进行操作
pos.x = pos.x * m.value[0][0] + pos.y * m.value[0][1] + pos.z * m.value[0][2] + pos.w * m.value[0][3];
pos.y = pos.x * m.value[1][0] + pos.y * m.value[1][1] + pos.z * m.value[1][2] + pos.w * m.value[1][3];
pos.z = pos.x * m.value[2][0] + pos.y * m.value[2][1] + pos.z * m.value[2][2] + pos.w * m.value[2][3];
pos.w = pos.x * m.value[3][0] + pos.y * m.value[3][1] + pos.z * m.value[3][2] + pos.w * m.value[3][3];
//注意,只有旋转+平移才能直接使用MVP矩阵,如果产生了变形,法线再这样乘就是错的
normal.x = normal.x * m.value[0][0] + normal.y * m.value[0][1] + normal.z * m.value[0][2] + normal.w * m.value[0][3];
normal.y = normal.x * m.value[1][0] + normal.y * m.value[1][1] + normal.z * m.value[1][2] + normal.w * m.value[1][3];
normal.z = normal.x * m.value[2][0] + normal.y * m.value[2][1] + normal.z * m.value[2][2] + normal.w * m.value[2][3];
normal.w = normal.x * m.value[3][0] + normal.y * m.value[3][1] + normal.z * m.value[3][2] + normal.w * m.value[3][3];
return *this;
}
MyMath类---关键定义了重心坐标的求法
作者在此定义了一个clamp方法来限制x的大小
同时定义了重要的方法——求重心坐标
#pragma once
#include"Vector3.h"
#define PI 3.1415926535
//将x限制在mi~ma的范围内
float clamp(float x, float mi, float ma);
//已知p,求其在v1,v2,v3组成的三角形内的重心坐标
Vector3f centerOfGravity(Vector3f v1, Vector3f v2, Vector3f v3, Vector2 p);
#include"MyMath.h"
float clamp(float x, float mi, float ma)
{
if (x < mi)x = mi;
if (x > ma)x = ma;
return x;
}
Vector3f centerOfGravity(Vector3f v1, Vector3f v2, Vector3f v3, Vector2 p)
{
//这两种情况说明三角形面积为0
if ((-(v1.x - v2.x) * (v3.y - v2.y) + (v1.y - v2.y) * (v3.x - v2.x)) == 0)
return Vector3f(1, 0, 0);
if (-(v2.x - v3.x) * (v1.y - v3.y) + (v2.y - v3.y) * (v1.x - v3.x) == 0)
return Vector3f(1, 0, 0);
//重心坐标
float alpha = (-(p.x - v2.x) * (v3.y - v2.y) + (p.y - v2.y) * (v3.x - v2.x)) / (-(v1.x - v2.x) * (v3.y - v2.y) + (v1.y - v2.y) * (v3.x - v2.x));
float beta = (-(p.x - v3.x) * (v1.y - v3.y) + (p.y - v3.y) * (v1.x - v3.x)) / (-(v2.x - v3.x) * (v1.y - v3.y) + (v2.y - v3.y) * (v1.x - v3.x));
float gamma = 1 - alpha - beta;
return Vector3f(alpha, beta, gamma);
}
Transform类---定义平移旋转缩放矩阵
封装了平移,旋转,缩放所需的参数
定义了成员ObjectToWorld矩阵
定义了根据传参构造T,R,S矩阵的方法
#pragma once
#include"Matrix.h"
#include"MyMath.h"
class Transform {
public:
//平移、旋转缩放参数
Vector3f position;
Vector3f rotation;
Vector3f scale;
Matrix objectToWorld;
//(不仅要构造出矩阵,还要把参数赋值给对应的成员变量)
Matrix Translate(const Vector3f& v); //平移(根据传的参数v构造平移矩阵)
//根据angle构造绕X/Y/Z轴旋转的矩阵
Matrix RotateX(float angle);
Matrix RotateY(float angle);
Matrix RotateZ(float angle);
//封装了RotateX Y Z,根据传进来的三个角去旋转
Matrix Rotate(const Vector3f& rotAngle);
//缩放(根据传的参数s构造缩放矩阵)
Matrix Scale(const Vector3f& s);
Transform(Vector3f pos, Vector3f rot, Vector3f s) :position(pos), rotation(rot), scale(s) {}
Transform() { objectToWorld.Identity(); }
};
#include"Transform.h"
#include <cmath>
Matrix Transform::Translate(const Vector3f& v) {
position = v;
Matrix m;
m.Identity();
//每行的第四列负责平移
m.value[0][3] = v.x;
m.value[1][3] = v.y;
m.value[2][3] = v.z;
return m;
}
Matrix Transform::RotateX(float angle)
{
rotation.x = angle;
Matrix m;
m.Identity();
float radian = angle / 360.0f * PI;
float cosValue = cos(radian);
float sinValue = sin(radian);
m.value[1][1] = cosValue;
m.value[1][2] = -sinValue;
m.value[2][1] = sinValue;
m.value[2][2] = cosValue;
return m;
}
Matrix Transform::RotateY(float angle)
{
rotation.y = angle;
Matrix m;
m.Identity();
float radian = angle / 360.0f * PI;
float cosValue = cos(angle);
float sinValue = sin(angle);
m.value[0][0] = cosValue;
m.value[0][2] = sinValue;
m.value[2][0] = -sinValue;
m.value[2][2] = cosValue;
return m;
}
Matrix Transform::RotateZ(float angle)
{
rotation.z = angle;
Matrix m;
m.Identity();
float radian = angle / 360.0f * PI;
float cosValue = cos(angle);
float sinValue = sin(angle);
m.value[0][0] = cosValue;
m.value[0][1] = -sinValue;
m.value[1][0] = sinValue;
m.value[1][1] = cosValue;
return m;
}
Matrix Transform::Rotate(const Vector3f& rotAngle)
{
rotation = rotAngle;
Matrix rotX = RotateX(rotAngle.x);
Matrix rotY = RotateY(rotAngle.y);
Matrix rotZ = RotateZ(rotAngle.z);
return rotX * rotY * rotZ; //同时执行三种变换
}
Matrix Transform::Scale(const Vector3f& s)
{
Matrix m;
scale = s;
m.Identity();
m.value[0][0] = s.x;
m.value[1][1] = s.y;
m.value[2][2] = s.z;
return m;
}
Mesh类---用于创建模型
含成员变量Transform transform
数组vertexBuffer 顶点
positionBuffer 顶点坐标
normalBuffer 法线
uvBuffer uv坐标
indexBuffer f中三个下标
以上这些除了顶点都读取自.obj模型文件
#pragma once
#include"Vertex.h"
#include"Transform.h"
#include<vector>
class Mesh {
public:
Transform transform;
std::vector<Vertex> vertexBuffer;
std::vector<Vector3f>positionBuffer;
std::vector<Vector3f>normalBuffer;
std::vector<Vector2>uvBuffer;
std::vector<Vector3i>indexBuffer;
Mesh();
~Mesh();
Transform GetTransform() { return transform; }
int GetIndexBufferLength() { return indexBuffer.size(); }
void SetTransform(Transform& t) { transform = t;}
Matrix GetObjectToWorld() { return transform.objectToWorld; }
void SetObjectToWorld(const Matrix& m) { transform.objectToWorld = m; }
void AddVertexData(const Vector3f pos, float u, float v, const Color color = Color::white);
void AddVertexData(float posx, float posy, float posz, float u, float v, const Color color = Color::white);
void AddVertexData(float posx, float posy, float posz, float u, float v, Vector3f nor, const Color color = Color::white);
};
#include "Mesh.h"
Mesh::Mesh()
{
}
Mesh::~Mesh()
{
}
void Mesh::AddVertexData(const Vector3f pos, float u, float v, const Color color)
{
Vertex p(pos, color, Vector2(u, v));
vertexBuffer.push_back(p);
}
void Mesh::AddVertexData(float posx, float posy, float posz, float u, float v, const Color color)
{
AddVertexData(Vector3f(posx, posy, posz), u, v, color);
}
void Mesh::AddVertexData(float posx, float posy, float posz, float u, float v, Vector3f nor, const Color color)
{
Vertex p(Vector3f(posx, posy, posz), color, nor, Vector2(u, v));
vertexBuffer.push_back(p);
}
Buffer类---深度缓存
定义了二维数组depthBuffer
定义了Sample方法
#pragma once
struct Buffer {
int height, width;
};
struct DepthBuffer :Buffer {
float** depthBuffer; //深度缓存(二维数组)
DepthBuffer(int width, int height) {
this->height = height;
this->width = width;
//为depthBuffer分配空间
depthBuffer = new float* [height];
for (int i = 0; i < height; i++) {
depthBuffer[i] = new float[width];
}
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
depthBuffer[i][j] = 1; //深度缓存初始化为1
}
}
}
//将value限制在min和max内
float Clamp(float min, float max, float value)
{
if (value > max)
value = max;
if (value < min)
value = min;
return value;
}
//采样
float Sample(float u, float v) {
int y = Clamp(0, height - 1, u);
int x = Clamp(0, width - 1, v);
return depthBuffer[y][x];
}
~DepthBuffer() {
for (int i = 0; i < height; i++)
{
delete[] depthBuffer[i];
}
}
};
插入知识点 ---视图变换过程
避免之后产生混淆,在此复习一下视图变换的过程
Light类---定义光源
定义了两种光的类——平行光和点光源(现在主要只看了平行光)
属性有光照强度、光源位置、光源方向等
一个bool变量来控制着色方式——逐顶点or逐像素
方法----Matrix LookAt(const Vector3f& upAxis) 这个和相机那个类似
#pragma once
#include"Matrix.h"
//平行光
class DirectionLight {
private:
Vector3f direction; //光照方向
Vector3f position; //光源位置
public:
float intensity; //光照强度
bool forVertex; //逐顶点或逐像素光照着色
//默认构造:光照强度为1,逐顶点着色
DirectionLight() { intensity = 1; forVertex = true; }
DirectionLight(const Vector3f& dir, const Vector3f& pos, float Intensity = 1, bool forV = true)
{
direction = dir;
position = pos;
intensity = Intensity;
forVertex = forV;
}
};
//点光源
class PointLight
{
public:
PointLight() { intensity = 1.0; }
PointLight(const Vector3f& pos, float inten) :position(pos), intensity(inten) {}
~PointLight() {}
Vector3f position;
float intensity;
};
#include"Light.h"
Matrix DirectionLight::LookAt(const Vector3f& upAxis)
{
Vector3f lookDir = direction;
lookDir.Normalize();
Vector3f rightDir = Vector3f::Cross(upAxis, lookDir);
rightDir.Normalize();
Vector3f upDir = Vector3f::Cross(lookDir, rightDir);
upDir.Normalize();
Matrix m;
m.value[0][0] = rightDir.x; m.value[1][0] = upDir.x; m.value[2][0] = lookDir.x; m.value[3][0] = 0;
m.value[0][1] = rightDir.y; m.value[1][1] = upDir.y; m.value[2][1] = lookDir.y; m.value[3][1] = 0;
m.value[0][2] = rightDir.z; m.value[1][2] = upDir.z; m.value[2][2] = lookDir.z; m.value[3][2] = 0;
m.value[0][3] = -position.x; m.value[1][3] = -position.y; m.value[2][3] = -position.z; m.value[3][3] = 1;
return m;
}
Camera类---用于观测的摄像机
有Transform类transform,矩阵v,矩阵p成员
定义了三个方法:透视矩阵(两种定义方法),正交矩阵,摄像机变换LookAt矩阵
透视投影矩阵的推导结论
#pragma once
#include"Transform.h"
class Camera {
public:
Transform transform; //相当于M矩阵
Matrix v, p;
Camera(Transform t):transform(t){}
Camera() {}
//LookAt矩阵
Matrix LookAt(const Vector3f& eyePos, const Vector3f& lookat, const Vector3f& upAxis);
//透视和正交矩阵
Matrix Perspective(float fov, float aspect, float nearPanel, float farPanel);
Matrix Perspective(float l, float r, float n, float f, float t, float b);
Matrix Orthographic(float l, float r, float n, float f, float t, float b);
};
#include"Camera.h"
#include <cmath>
Matrix Camera::LookAt(const Vector3f& eyePos, const Vector3f& lookat, const Vector3f& upAxis)
{
Vector3f lookDir = lookat;
lookDir.Normalize();
Vector3f rightDir = Vector3f::Cross(upAxis, lookDir);
rightDir.Normalize();
Vector3f upDir = Vector3f::Cross(lookDir, rightDir);
upDir.Normalize();
//自此,获得了相机的坐标系
Matrix m;
//原坐标系变换到相机坐标系的 逆 矩阵(也就是相机到原点)
m.value[0][0] = rightDir.x; m.value[1][0] = upDir.x; m.value[2][0] = lookDir.x; m.value[3][0] = 0;
m.value[0][1] = rightDir.y; m.value[1][1] = upDir.y; m.value[2][1] = lookDir.y; m.value[3][1] = 0;
m.value[0][2] = rightDir.z; m.value[1][2] = upDir.z; m.value[2][2] = lookDir.z; m.value[3][2] = 0;
m.value[0][3] = -eyePos.x; m.value[1][3] = -eyePos.y; m.value[2][3] = -eyePos.z; m.value[3][3] = 1;
v = m; //给成员v赋值
return v;
}
//https://zhuanlan.zhihu.com/p/122411512
//视锥角,宽高比,远近平面定义的投影转正交->正交
Matrix Camera::Perspective(float fov, float aspect, float nearPanel, float farPanel)
{
float tanValue = tan(0.5f * fov * PI / 180);
Matrix proj;
proj.value[0][0] = 1 / (tanValue * aspect);
proj.value[1][1] = 1/tanValue;
proj.value[2][2] = (nearPanel + farPanel) / (nearPanel - farPanel);
proj.value[2][3] = -2 * nearPanel * farPanel / (nearPanel - farPanel);
proj.value[3][2] = 1;
proj.value[3][3] = 0;
p = proj;
return proj;
}
//几个面定义的投影转正交-->正交
Matrix Camera::Perspective(float l, float r, float n, float f, float t, float b)
{
Matrix m;
m.value[0][0] = 2 * n / (r - l); m.value[0][1] = 0; m.value[0][2] = (l + r) / (l - r); m.value[0][3] = 0;
m.value[1][0] = 0; m.value[1][1] = 2 * n / (t - b); m.value[1][2] = (b + t) / (b - t); m.value[1][3] = 0;
m.value[2][0] = 0; m.value[2][1] = 0; m.value[2][2] = (n + f) / (n - f); m.value[2][3] = 2 * n * f / (f - n);
m.value[3][0] = 0; m.value[3][1] = 0; m.value[3][2] = 1; m.value[3][3] = 0;
p = m;
return m;
}
//正交投影(缩放*平移矩阵得到的总矩阵)
Matrix Camera::Orthographic(float l, float r, float n, float f, float t, float b)
{
Matrix m;
m.value[0][0] = 2 / (r - l); m.value[0][1] = 0; m.value[0][2] = 0; m.value[0][3] = -(r + l) / (r - l);
m.value[1][0] = 0; m.value[1][1] = 2 / (t - b); m.value[1][2] = 0; m.value[1][3] = -(t + b) / (t - b);
m.value[2][0] = 0; m.value[2][1] = 0; m.value[2][2] = 2 / (n - f); m.value[2][3] = -(n + f) / (n - f);
m.value[3][0] = 0; m.value[3][1] = 0; m.value[3][2] = 0; m.value[3][3] = 1;
p = m;
return m;
}
Input类---检测键盘输入
负责检测键盘的输入
#pragma once
#define IS_KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define IS_KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
Texture类---加载并存储纹理
用于加载纹理
定义了成员变量---二维数组textureData,到时候就是要通过textureData[u][v]来访问纹理
定义成员方法---Sample(和Buffer里的Sample思想一样儿)和LoadTexture(存到textureData里)
#pragma once
#include"Color.h"
class Color;
class Texture {
private:
int width;
int height;
Color textureData[1024][1024];
public:
Texture();
~Texture();
//根据路径打开纹理图
void LoadTexture(const char*path);
Color Sample(float u, float v);
float Clamp(float min, float max, float value);
};
#include "Texture.h"
#include<Windows.h>
Texture::Texture()
{
width = 512;
height = 512;
}
Texture::~Texture()
{
}
void Texture::LoadTexture(const char* path)
{
//加载位图
HBITMAP bitmap = (HBITMAP)LoadImage(NULL, path, IMAGE_BITMAP, width, height, LR_LOADFROMFILE);
//hdc可标识这张图
HDC hdc = CreateCompatibleDC(NULL);
SelectObject(hdc, bitmap);
//获取到纹理图的颜色,赋值给textureData成员
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
//RGB转为int
COLORREF color = GetPixel(hdc, i, j);
int r = color % 256;
int g = (color >> 8) % 256;
int b = (color >> 16) % 256;
Color c((float)r / 256, (float)g / 256, (float)b / 256, 1);
textureData[i][j] = c;
}
}
}
Color Texture::Sample(float u, float v)
{
//把u v限制在0,1
u = Clamp(0, 1.0f, u);
v = Clamp(0, 1.0f, v);
int intu = width * u;
int intv = height * v;
return textureData[intu][intv];
}
float Texture::Clamp(float min, float max, float value)
{
if (value > max)
value = max;
if (value < min)
value = min;
return value;
}
插入知识点----shadow map算法
将物体顶点通过矩阵运算转移到灯光空间下
在灯光坐标下渲染物体阴影:
创建一张深度贴图,用于保存物体在灯光空间下的深度信息。
创建过程:如果某个顶点的深度值比原来深度贴图中的值更小,就更新深度贴图,反之则丢弃。
获取到深度贴图后,如果某个点在光源视角下深度值大于深度贴图中对应位置的深度值,说明它在阴影中。那么就对该顶点乘上阴影的颜色
Uniform类
一致性变量
三个类:
- PhongVert
- 光照的数组
- cameraPos观测位置
- mvp矩阵
- 三个顶点
- shadow map要用的lp0,1,2 lightV lightP矩阵
- PhongFrag
- 纹理指针
- 深度缓存指针
- ShadowVert
- mvp矩阵
#pragma once
#include <vector>
#include "Light.h"
#include "Vector3.h"
#include "Texture.h"
#include "Matrix.h"
#include "Buffer.h"
struct PhongVert {
std::vector<DirectionLight>dirlights; //直射光数组
std::vector<PointLight>pointlights; //点光源数组
Vector3f cameraPos; //相机观测位置
Vector3f lp0, lp1, lp2; //站在光位置的坐标-用于计算shadow map
Matrix m, v, p; //视图变换矩阵
Vertex v0, v1, v2; //三个顶点信息
Matrix lightV, lightP; //站在光位置的VP矩阵-用于计算shadow map
};
struct PhongFrag {
Texture* baseTex; //基础纹理
DepthBuffer* depthBuffer; //深度缓存
PhongFrag(Texture* baseTex, DepthBuffer* depthBuffer) :baseTex(baseTex), depthBuffer(depthBuffer) {}
~PhongFrag() {
}
};
struct ShadowVert {
Matrix lightV, lightP; //灯光视角的MVP矩阵
Matrix m;
};
接下来是最核心的两个类
三、软光栅封装
Shader类
两个类(其中都含有函数顶点着色器VS和片元着色器FS):
- PhongShader
- PhongVert v2f
- PhongFrag frag
- CalcuteShadow函数
- ShadowShader
- ShadowVert v2f
#pragma once
#include"MyMath.h"
#include"Vertex.h"
#include"Uniform.h"
//抽象类IShader
struct IShader {
public:
//顶点着色器
virtual void VS(Vertex& v0, Vertex& v1, Vertex& v2) = 0;
//片元着色器
virtual bool FS(Vertex& v, Vector3f g) = 0;
};
struct PhongShader :public IShader {
public:
PhongVert v2f; //vertex to fragment
PhongFrag frag;
public:
PhongShader(PhongVert v2f,PhongFrag frag):v2f(v2f),frag(frag){}
~PhongShader(){}
//自IShader继承
virtual void VS(Vertex& v0, Vertex& v1, Vertex& v2) override;
virtual bool FS(Vertex& v, Vector3f g) override;
float CalcuteShadow(Vector3f posLightSpace, double bias);
};
class ShadowShader :public IShader
{
public:
ShadowShader() {}
~ShadowShader() {}
// 通过 IShader 继承
virtual void VS(Vertex& v0, Vertex& v1, Vertex& v2) override;
virtual bool FS(Vertex& v, Vector3f g) override;
ShadowVert v2f;
};
#include "Shader.h"
using namespace std;
void PhongShader::VS(Vertex& v0, Vertex& v1, Vertex& v2)
{
//phongvert获取到参数的顶点
v2f.v0 = v0;
v2f.v1 = v1;
v2f.v2 = v2;
Vertex* v = &v0; //v用来遍历三个顶点
for (int i = 0; i < 3; i++)
{
switch (i)
{
//从顶点pos获取lp,并进行MVP变换
case 0:
v = &v0;
v2f.lp0 = v0.pos;
v2f.lp0 = v2f.m.MultiplyVector3(v2f.lp0);
v2f.lp0 = v2f.lightV.MultiplyVector3(v2f.lp0);
v2f.lp0 = v2f.lightP.MultiplyVector3(v2f.lp0);
break;
case 1:
v = &v1;
v2f.lp1 = v1.pos;
v2f.lp1 = v2f.m.MultiplyVector3(v2f.lp1);
v2f.lp1 = v2f.lightV.MultiplyVector3(v2f.lp1);
v2f.lp1 = v2f.lightP.MultiplyVector3(v2f.lp1);
break;
case 2:
v = &v2;
v2f.lp2 = v2.pos;
v2f.lp2 = v2f.m.MultiplyVector3(v2f.lp2);
v2f.lp2 = v2f.lightV.MultiplyVector3(v2f.lp2);
v2f.lp2 = v2f.lightP.MultiplyVector3(v2f.lp2);
break;
}
//顶点的M变换
v->pos = v2f.m.MultiplyVector3(v->pos);
//此处和transform中的法线处理存疑
Matrix nm = v2f.m;
//去掉平移.不考虑变形缩放的情况下使用,正常情况下使用逆的转置
nm.value[0][3] = 0;
nm.value[1][3] = 0;
nm.value[2][3] = 0;
v->normal = nm.MultiplyVector3(v->normal).Normalize();
//可选着色
float diffuse = 0; //漫反射
float specular = 0; //镜面反射
float ambient = 0.1;//环境光
//直射光
for (auto light : v2f.dirlights)
{
Vector3f l = light.GetDirection().Normalize();
diffuse += max(0.f, l.Dot(v->normal)) * light.intensity;
Vector3f h = ((v2f.cameraPos - v->pos).Normalize() + l).Normalize();
specular += pow(max(0.f, v->normal.Dot(h)), 1) * light.intensity;
//diffuse+ specular超过1会出现渲染错误
}
//点光源
// for (auto light : v2f.pointlights)
// {
// Vector3f l = (light.position-v->pos).Normalize();
// diffuse += max(0, l.Dot(v->normal))*light.intensity*min(1, 1 / (light.position - v->pos).Magnitude());
//
// Vector3f h = ((v2f.cameraPos - v->pos).Normalize() + (light.position-v->pos).Normalize()).Normalize();
// //h.Normalize();
//
// specular += pow(max(0, v->normal.Dot(h)), 1)*light.intensity*min(1, 1 / (light.position - v->pos).Magnitude());
// //float k = (specular + diffuse) *light.intensity*min(1, 1 / (light.position - v.pos).Magnitude());
// //cout << k << endl;
// //cout << diffuse <<" "<< specular << endl; Color::white+
// // v.color + specular + diffuse;
// //diffuse+ specular超过1会出现渲染错误
// //v.color =v.color*(min(1, k));// Color(specular + diffuse, specular + diffuse, specular + diffuse, 1);
// //Color(diffuse+specular, diffuse + specular, diffuse + specular, 1);
// //cout << v.color.r << " "<<v.color.g << " " << v.color.b << " " << endl;
// }
v->color = v->color * (min(1.f, specular + diffuse + ambient)); //颜色乘上光照
//VP变换
v->pos = v2f.v.MultiplyVector3(v->pos);
v->pos = v2f.p.MultiplyVector3(v->pos);
v->pos.standardization();
}
//保证v0.y v1.y v2.y的顺序
if (v1.pos.y < v0.pos.y)
{
std::swap(v2f.lp0, v2f.lp1);
std::swap(v1, v0);
}
if (v2.pos.y < v0.pos.y)
{
std::swap(v2f.lp0, v2f.lp2);
std::swap(v2, v0);
}
if (v2.pos.y < v1.pos.y)
{
std::swap(v2f.lp1, v2f.lp2);
std::swap(v2, v1);
}
}
bool PhongShader::FS(Vertex& v, Vector3f g)
{
//颜色乘上纹理信息
v.color = v.color * frag.baseTex->Sample(v.uv.x, v.uv.y);
//法线插值
Vector3f normal = v2f.v0.normal * g.x + v2f.v1.normal * g.y + v2f.v2.normal * g.z;
//光照空间下的插值
Vector3f posLightSpace = v2f.lp0 * g.x + v2f.lp1 * g.y + v2f.lp2 * g.z;
//阴影
//https://juejin.cn/post/7021462579859947527
float bias = 0.005; //引入偏移量bias的原因:避免Shadow ance
if (v2f.dirlights.size() > 0)
{
bias = max(0.02 * (1.0 - abs(Vector3f::Dot(normal.Normalize(), v2f.dirlights[0].GetDirection().Normalize()))), 0.005);
//Slope Scale Based Depth Bias
}
float depth = CalcuteShadow(posLightSpace, bias);
v.color = v.color * (1 - depth);
return v.color.a > 0;
}
//bias可以通过角度计算
float PhongShader::CalcuteShadow(Vector3f posLightSpace, double bias)
{
float reciprocalW = 1.0f / posLightSpace.w;
//加0.5做之后的四舍五入
posLightSpace.x = (posLightSpace.x * reciprocalW + 1.0f) * 0.5f * (frag.depthBuffer->width - 1) + 0.5;
posLightSpace.y = (posLightSpace.y * reciprocalW + 1.0f) * 0.5f * (frag.depthBuffer->height - 1) + 0.5;
float depth = (posLightSpace.z + 1.0) / 2.0;
//此处可以有PCF优化
float shadow = 0.0;
//普通阴影
float closestDepth = frag.depthBuffer->Sample(posLightSpace.y, posLightSpace.x);
shadow = depth - bias > closestDepth ? 1 : 0;
return shadow;
}
void ShadowShader::VS(Vertex& v0, Vertex& v1, Vertex& v2)
{
Vertex* v = &v1;
for (int i = 0; i < 3; i++)
{
switch (i)
{
case 0:
v = &v0;
break;
case 1:
v = &v1;
break;
case 2:
v = &v2;
break;
}
//MVP矩阵
v->pos = v2f.m.MultiplyVector3(v->pos);
v->pos = v2f.lightV.MultiplyVector3(v->pos);
v->pos = v2f.lightP.MultiplyVector3(v->pos);
v->pos.standardization();
}
if (v1.pos.y < v0.pos.y)
{
std::swap(v1, v0);
}
if (v2.pos.y < v0.pos.y)
{
std::swap(v2, v0);
}
if (v2.pos.y < v1.pos.y)
{
std::swap(v2, v1);
}
}
bool ShadowShader::FS(Vertex& v, Vector3f g)
{
//啥都不做
return false;
}
Renderer类
texture指针
和开窗口有关的变量
光照数组
相机
#pragma once
#include "Vertex.h"
#include "Light.h"
#include "Texture.h"
#include "Camera.h"
#include"Shader.h"
#include "Mesh.h"
#include "Buffer.h"
#include<Windows.h>
using namespace std;
class Renderer
{
private:
int deviceWidth;
int deviceHeight;
HDC screenHDC;
Texture* tex;
public:
vector<DirectionLight> directionlights;
vector<PointLight> pointlights;
Camera* camera;
public:
Renderer(HDC hdc, int screenWidth, int screenHeight, Camera* cam);
~Renderer();
//每渲染一次,都要清除深度缓存
void Clear(DepthBuffer* zbuffer);
//根据indexBuffer绘制
void DrawByIndex(Mesh* m, IShader* shader, DepthBuffer* zbuffer);
//根据顶点绘制
void DrawByArray(Mesh* m, IShader* shader, DepthBuffer* zbuffer);
void DrawMesh(Mesh* m, IShader* shader, DepthBuffer* zbuffer);
void DrawPrimitive(Vertex v0, Vertex v1, Vertex v2, IShader* shader, DepthBuffer* zbuffer);
void RasterizeTrangle(Vertex v0, Vertex v1, Vertex v2, IShader* shader, DepthBuffer* zbuffer);
//swapIndex代表第几个点是插值出来的
void DrawTopFlatTrangle(Vertex v0, Vertex v1, Vertex v2, IShader* shader, DepthBuffer* zbuffer, Vertex v3, int swapIndex);
void DrawBottomFlatTrangle(Vertex v0, Vertex v1, Vertex v2, IShader* shader, DepthBuffer* zbuffer, Vertex v3, int swapIndex);
void DrawLine(Vertex v0, Vertex v1, IShader* shader, DepthBuffer* zbuffer, Vector3f p0, Vector3f p1, Vector3f p2);
void DrawPixel(int x, int y, const Color& color);
bool ZTestAndWrite(int x, int y, float depth, DepthBuffer* zbuffer);
//准备光栅化,透视投影除法,视口映射,三角形数据准备
void PrepareRasterization(Vertex& vertex, Buffer* buffer);
//简单CVV剔除,只考虑三顶点均不在的情况,未做边界三角形重新构建
bool SimpleCVVCullCheck(const Vertex& vertex);
};
#include "Renderer.h"
#include "Matrix.h"
#include <iostream>
#include <Windows.h>
using namespace std;
Renderer::Renderer(HDC hdc, int screenWidth, int screenHeight, Camera* cam)
{
screenHDC = hdc;
deviceWidth = screenWidth;
deviceHeight = screenHeight;
camera = cam;
tex = new Texture();
tex->LoadTexture("gezi.bmp");
}
Renderer::~Renderer()
{
}
void Renderer::DrawByIndex(Mesh* m, IShader* shader, DepthBuffer* zbuffer)
{
for (int i = 0; i < m->indexBuffer.size(); i = i + 3)
{
Vertex p1;
p1.pos = m->positionBuffer[m->indexBuffer[i].x - 1];
p1.uv = m->uvBuffer[m->indexBuffer[i].y - 1];
p1.normal = m->normalBuffer[m->indexBuffer[i].z - 1];
Vertex p2;
p2.pos = m->positionBuffer[m->indexBuffer[i + 1].x - 1];
p2.uv = m->uvBuffer[m->indexBuffer[i + 1].y - 1];
p2.normal = m->normalBuffer[m->indexBuffer[i + 1].z - 1];
Vertex p3;
p3.pos = m->positionBuffer[m->indexBuffer[i + 2].x - 1];
p3.uv = m->uvBuffer[m->indexBuffer[i + 2].y - 1];
p3.normal = m->normalBuffer[m->indexBuffer[i + 2].z - 1];
DrawPrimitive(p1, p2, p3, shader, zbuffer);
}
}
void Renderer::DrawByArray(Mesh* m, IShader* shader, DepthBuffer* zbuffer)
{
for (int i = 0; i < m->vertexBuffer.size(); i = i + 3)
{
Vertex p1 = m->vertexBuffer[i];
Vertex p2 = m->vertexBuffer[i + 1];
Vertex p3 = m->vertexBuffer[i + 2];
DrawPrimitive(p1, p2, p3, shader, zbuffer);
}
}
void Renderer::DrawMesh(Mesh* m, IShader* shader, DepthBuffer* zbuffer)
{
if (m->indexBuffer.size() > 0)
DrawByIndex(m, shader, zbuffer);
else
DrawByArray(m, shader, zbuffer);
}
void Renderer::DrawPrimitive(Vertex v1, Vertex v2, Vertex v3, IShader* shader, DepthBuffer* zbuffer)
{
shader->VS(v1, v2, v3);
//进行CVV简单剔除判断
// if (SimpleCVVCullCheck(v1) && SimpleCVVCullCheck(v2) && SimpleCVVCullCheck(v3))
// return;
//透视除法,视口映射,数据准备(全部改为1/z)
PrepareRasterization(v1, zbuffer);
PrepareRasterization(v2, zbuffer);
PrepareRasterization(v3, zbuffer);
//三角形三个点重合明显存在问题,直接不渲染,或者两个
if (((int)v1.pos.y == (int)v2.pos.y && abs(v2.pos.y - v3.pos.y) <= 1) ||
((int)v3.pos.y == (int)v2.pos.y && abs(v2.pos.y - v1.pos.y) <= 1) ||
((int)v1.pos.y == (int)v3.pos.y && abs(v2.pos.y - v3.pos.y) <= 1))
return;
RasterizeTrangle(v1, v2, v3, shader, zbuffer);
}
void Renderer::RasterizeTrangle(Vertex v0, Vertex v1, Vertex v2, IShader* shader, DepthBuffer* zbuffer)
{
int ty0 = v0.pos.y;
int ty1 = v1.pos.y;
int ty2 = v2.pos.y;
if (ty0 == ty1) //上三角形
{
DrawTopFlatTrangle(v0, v1, v2, shader, zbuffer, Vertex(), -1);
}
else if (ty1 == ty2) //下三角形
{
DrawBottomFlatTrangle(v0, v1, v2, shader, zbuffer, Vertex(), -1);
}
else//拆分为一个平顶三角形和一个平底三角形
{
//中心点为直线(x0, y0),(x2, y2)上取y1的点
float x3 = (v1.pos.y - v0.pos.y) * (v2.pos.x - v0.pos.x) / (v2.pos.y - v0.pos.y) + v0.pos.x;
float y3 = v1.pos.y;
float t = (y3 - v0.pos.y) / (v2.pos.y - v0.pos.y);
Vertex v3(Vector3f(x3, y3, 0), Color(0, 0, 0, 0), Vector2(0, 0));
v3.LerpVertexData(v0, v2, t);
DrawBottomFlatTrangle(v0, v1, v3, shader, zbuffer, v2, 3);
DrawTopFlatTrangle(v3, v1, v2, shader, zbuffer, v0, 1);
}
}
void Renderer::DrawTopFlatTrangle(Vertex v0, Vertex v1, Vertex v2, IShader* shader, DepthBuffer* zbuffer, Vertex v3, int swapIndex)
{
float x0 = v0.pos.x;
float y0 = v0.pos.y;
float x1 = v1.pos.x;
float y1 = v1.pos.y;
float x2 = v2.pos.x;
float y2 = v2.pos.y;
for (float y = y0; y <= y2; y++)
{
float t = (y - y0) / (y2 - y0);
//用int,不然会有断线
int xl = (y - y0) * (x2 - x0) / (y2 - y0) + x0;
Vertex vl(Vector3f(xl, y, 0), Color(0, 0, 0, 0), Vector2(0, 0));
vl.LerpVertexData(v0, v2, t);
int xr = (y - y1) * (x2 - x1) / (y2 - y1) + x1;
Vertex vr(Vector3f(xr, y, 0), Color(0, 0, 0, 0), Vector2(0, 0));
vr.LerpVertexData(v1, v2, t);
switch (swapIndex)
{
case -1:
DrawLine(vl, vr, shader, zbuffer, v0.pos, v1.pos, v2.pos);
break;
case 1:
DrawLine(vl, vr, shader, zbuffer, v3.pos, v1.pos, v2.pos);
break;
case 2:
DrawLine(vl, vr, shader, zbuffer, v0.pos, v3.pos, v2.pos);
break;
case 3:
DrawLine(vl, vr, shader, zbuffer, v0.pos, v1.pos, v3.pos);
break;
default:
DrawLine(vl, vr, shader, zbuffer, v0.pos, v1.pos, v2.pos);
break;
}
}
}
void Renderer::DrawBottomFlatTrangle(Vertex v0, Vertex v1, Vertex v2, IShader* shader, DepthBuffer* zbuffer, Vertex v3, int swapIndex)
{
float x0 = v0.pos.x;
float y0 = v0.pos.y;
float x1 = v1.pos.x;
float y1 = v1.pos.y;
float x2 = v2.pos.x;
float y2 = v2.pos.y;
for (float y = y0; y <= y1; y++)
{
float t = (y - y0) / (y2 - y0);
int xl = ((y - y1) * (x0 - x1) / (y0 - y1) + x1);
Vertex vl(Vector3f(xl, y, 0), Color(0, 0, 0, 0), Vector2(0, 0));
vl.LerpVertexData(v0, v1, t);
int xr = ((y - y2) * (x0 - x2) / (y0 - y2) + x2);
Vertex vr(Vector3f(xr, y, 0), Color(0, 0, 0, 0), Vector2(0, 0));
vr.LerpVertexData(v0, v2, t);
switch (swapIndex)
{
case -1:
DrawLine(vl, vr, shader, zbuffer, v0.pos, v1.pos, v2.pos);
break;
case 1:
DrawLine(vl, vr, shader, zbuffer, v3.pos, v1.pos, v2.pos);
break;
case 2:
DrawLine(vl, vr, shader, zbuffer, v0.pos, v3.pos, v2.pos);
break;
case 3:
DrawLine(vl, vr, shader, zbuffer, v0.pos, v1.pos, v3.pos);
break;
default:
DrawLine(vl, vr, shader, zbuffer, v0.pos, v1.pos, v2.pos);
break;
}
}
}
//y值永远相等的线
void Renderer::DrawLine(Vertex v0, Vertex v1, IShader* shader, DepthBuffer* zbuffer, Vector3f p0, Vector3f p1, Vector3f p2)
{
//std::cout << v0.color.r ;
float x0 = v0.pos.x;
float x1 = v1.pos.x;
float y0 = v0.pos.y;
float y1 = v1.pos.y;
//只考虑x方向扫描线即可
int dx = x1 - x0;
int stepx = 1;
if (dx < 0)
{
stepx = -1;
dx = -dx;
}
int x = x0;
int y = y0;
Vertex frag;
if (x1 == x0)
{
if (ZTestAndWrite(x, y, (v0.pos.z + 1) / 2.0, zbuffer))
{
Vector3f g = centerOfGravity(p0, p1, p2, Vector2(x, y));
if (shader->FS(v0, g))
{
DrawPixel(x, y, v0.color);
}
}
return;
}
for (int i = 0; i <= dx; i++)
{
float s = (x - x0) / (x1 - x0);
//透视矫正,https://zhuanlan.zhihu.com/p/144331875
float t = s * v0.pos.z / (s * v0.pos.z + (1 - s) * v1.pos.z);
float z = Vertex::LerpFloat(v0.pos.z, v1.pos.z, t);
z = (z + 1) / 2.0;
if (ZTestAndWrite(x, y, z, zbuffer))
{
Color c = Color::Lerp(v0.color, v1.color, t);
float u = Vertex::LerpFloat(v0.uv.x, v1.uv.x, t);
float v = Vertex::LerpFloat(v0.uv.y, v1.uv.y, t);
frag.pos = Vector3f(x, y, z);
frag.color = c;
frag.uv = Vector2(u, v);
Vector3f g = centerOfGravity(p0, p1, p2, Vector2(x, y));
if (shader->FS(frag, g))
{
DrawPixel(x, y, frag.color);
}
}
x += stepx;
}
}
bool Renderer::ZTestAndWrite(int x, int y, float depth, DepthBuffer* zbuffer)
{
if (x >= 0 && x < zbuffer->width && y >= 0 && y < zbuffer->height)
{
if (zbuffer->depthBuffer[y][x] >= depth)
{
zbuffer->depthBuffer[y][x] = depth;
return true;
}
}
return false;
}
inline void Renderer::DrawPixel(int x, int y, const Color& color)
{
//cout << color.r << " " << color.g << " " << color.b << " " << endl;
//color值超过1会自动进行tonemapping
SetPixel(screenHDC, x, y, RGB(255 * color.r, 255 * color.g, 255 * color.b));
}
void Renderer::Clear(DepthBuffer* zbuffer)
{
BitBlt(screenHDC, 0, 0, deviceWidth, deviceHeight, NULL, NULL, NULL, BLACKNESS);
//ClearZ
for (int i = 0; i < zbuffer->height; i++)
{
for (int j = 0; j < zbuffer->width; j++)
{
zbuffer->depthBuffer[i][j] = 1;
}
}
}
inline void Renderer::PrepareRasterization(Vertex& vertex, Buffer* buffer)
{
float reciprocalW = 1.0f / vertex.pos.w;
//最后加0.5是为了后面取证做四舍五入
vertex.pos.x = (vertex.pos.x * reciprocalW + 1.0f) * 0.5f * (buffer->width - 1) + 0.5;
vertex.pos.y = (vertex.pos.y * reciprocalW + 1.0f) * 0.5f * (buffer->height - 1) + 0.5;
}
inline bool Renderer::SimpleCVVCullCheck(const Vertex& vertex)
{
float w = vertex.pos.w;
if (vertex.pos.x < -w || vertex.pos.x > w)
return true;
if (vertex.pos.y < -w || vertex.pos.y > w)
return true;
if (vertex.pos.z < 0.0f || vertex.pos.z > w)
return true;
return false;
}
一切准备就绪,接下来就可以读取模型文件
ObjFileReader类---读取模型文件,存入Mesh的Buffer中
#pragma once
#include <vector>
#include <string>
#include <fstream>
#include"Mesh.h"
using namespace std;
//切分字符串
void StringSplit(string s, char splitchar,vector<string>& vec);
void ReadObjFile(string path, Mesh* obj);
#include "ObjFileReader.h"
#include<stdlib.h>
//根据splitchar去切分字符串,存到vec里
void StringSplit(string s, char splitchar, vector<string>& vec)
{
if (vec.size() > 0)//保证vec是空的
vec.clear();
int length = s.length();
int start = 0;
for (int i = 0; i < length; i++) {
if (s[i] == splitchar && i == 0)//第一个就遇到分割符
{
start += 1;
}
else if (s[i] == splitchar) {
//遇到分隔符
vec.push_back(s.substr(start, i - start));
start = i + 1;
}
else if (i == length - 1)//到达尾部
{
vec.push_back(s.substr(start, i + 1 - start));
}
}
}
void ReadObjFile(string path, Mesh* obj)
{
ifstream in(path);
string txt = "";
//能找到该文件
if (in) {
while (getline(in, txt)) {
//读取顶点坐标
if (txt[0] == 'v' && txt[1] == ' ') {
vector<string>num;
txt.erase(0, 2);
StringSplit(txt, ' ', num);
Vector3f pos;
pos = Vector3f((float)atof(num[0].c_str()), (float)atof(num[1].c_str()), (float)atof(num[2].c_str()));
obj->positionBuffer.push_back(pos);
}
//读取顶点法线
else if (txt[0] == 'v' && txt[1] == 'n') {
vector<string>num;
txt.erase(0, 3);
StringSplit(txt, ' ', num);
Vector3f n;
n = Vector3f((float)atof(num[0].c_str()), (float)atof(num[1].c_str()), (float)atof(num[2].c_str()));
obj->normalBuffer.push_back(n);
}
//读取uv坐标
else if (txt[0] == 'v' && txt[1] == 't')
{
vector<string> num;
txt.erase(0, 3);
StringSplit(txt, ' ', num);
Vector2 uv;
uv=Vector2((float)atof(num[0].c_str()), (float)atof(num[1].c_str()));
obj->uvBuffer.push_back(uv);
}
//读取索引编号,'/'分隔的分别是每个面的顶点 该顶点uv坐标和法线坐标
else if (txt[0] == 'f' && txt[1] == ' ') {
vector<string>num;
txt.erase(0, 2);
StringSplit(txt, ' ', num);
for (int i = 0; i < num.size(); i++) {
vector<string> threeIndex;
StringSplit(num[i], '/', threeIndex);
Vector3i indexes = { atoi(threeIndex[0].c_str()), atoi(threeIndex[1].c_str()), atoi(threeIndex[2].c_str()) };
obj->indexBuffer.push_back(indexes);
}
}
}
}
else {
cout << "no file" << endl;
}
}
四、主函数及测试效果
#include "Renderer.h"
#include "ObjFileReader.h"
#include "Window.h"
#include "Input.h"
#include <iostream>
#include <cmath>
#pragma comment( lib,"winmm.lib" )
using namespace std;
static const int windowWidth = 500;
static const int windowHeight = 500;
Renderer* device = NULL;
Camera* camera = new Camera(Transform(Vector3f(0,0, 100), Vector3f(0, 0, 0), Vector3f(0, 0, 0))); //右边是相机的位置
Mesh* currentMesh = new Mesh();
Mesh* m = new Mesh();
Mesh* plane = new Mesh();
Vector3f moveVector, rotateVector; //根据键盘输入去变换
PhongShader* phongShader;
PhongShader* planeShader;
ShadowShader* depthShader = new ShadowShader();
DepthBuffer* depthBuffer = new DepthBuffer(windowWidth * 2, windowHeight * 2);
DepthBuffer* zBuffer = new DepthBuffer(windowWidth, windowHeight);
Matrix M;
void UpdateInput(Mesh* m)
{
if (IS_KEY_DOWN('A'))
{
moveVector.x -= 0.01f;
}
if (IS_KEY_DOWN('D'))
{
moveVector.x += 0.01f;
}
if (IS_KEY_DOWN('W'))
{
moveVector.y += 0.01f;
}
if (IS_KEY_DOWN('S'))
{
moveVector.y -= 0.01f;
}
if (IS_KEY_DOWN('E'))
{
moveVector.z -= 0.1f;
}
if (IS_KEY_DOWN('Q'))
{
moveVector.z += 0.1f;
}
if (IS_KEY_DOWN('I'))
{
rotateVector.y += 0.1f;
}
if (IS_KEY_DOWN('K'))
{
rotateVector.y -= 0.1f;
}
if (IS_KEY_DOWN('L'))
{
rotateVector.x += 0.5f;
}
if (IS_KEY_DOWN('J'))
{
rotateVector.x -= 0.5f;
}
if (IS_KEY_DOWN('U'))
{
rotateVector.z -= 0.1f;
}
if (IS_KEY_DOWN('O'))
{
rotateVector.z += 0.1f;
}
Matrix s = m->GetTransform().Scale(Vector3f(1, 1, 1));
Matrix r = m->GetTransform().Rotate(rotateVector);
Matrix t = m->GetTransform().Translate(moveVector);
//注意矩阵乘法顺序!!
M = t * r * s;
phongShader->v2f.m = M;
depthShader->v2f.m = M;
}
void Update(Window* w);
void DoRender(Window* w);
void ShowFPS(Window* w);
void CreateCube();
void CreatePlane();
int main()
{
Window* w = new Window(windowWidth, windowHeight, "Test");
device = new Renderer(w->screenHDC, windowWidth, windowHeight, camera);
//规定相机永远往-z方向看,这决定zbuffer初始化为最大值还是最小值(最大值,因为深度值为负数)
//视口的范围应该是0-负无穷,相反,如果往z轴方向看,视口的范围应该是0-正无穷
Matrix cameraV = camera->LookAt(camera->transform.position, Vector3f(0, 0, -1), Vector3f(0, 1, 0));
//这里远近平面的值相对于物体变换到相机坐标系的位置,范围从相机位置-1到-120,此时物体的位置在-100左右,近平面越靠近-100,深度值越趋近于1,,相反越趋近于-1
//Matrix cameraP = camera->Perspective(0.1, 0.1, -1, -120, 0.1, -0.1);
//Matrix cameraP = camera->Perspective(90, 1, 1, 10);
Matrix cameraP = camera->Orthographic(-10, 10, 0, -120, 10, -10);
Matrix lightP = camera->Orthographic(-10, 10, 0, -120, 10, -10);
DirectionLight light(Vector3f(0.2, 0.2, -1), Vector3f(0, 0, 100));
//PointLight poingt(Vector3f(5, 5, -5), 2);
Texture* gezi = new Texture();
gezi->LoadTexture("gezi.bmp");
phongShader = new PhongShader(PhongVert(), PhongFrag(gezi, depthBuffer));
phongShader->v2f.cameraPos = camera->transform.position;
phongShader->v2f.dirlights.push_back(light);
//phongShader->v2f.pointlights.push_back(poingt);
phongShader->v2f.v = cameraV;
phongShader->v2f.p = cameraP;
phongShader->v2f.lightV = light.LookAt(Vector3f(0, 1, 0));
phongShader->v2f.lightP = lightP;
depthShader->v2f.lightV = light.LookAt(Vector3f(0, 1, 0));
depthShader->v2f.lightP = lightP;
planeShader = new PhongShader(PhongVert(), PhongFrag(gezi, depthBuffer));;
planeShader->v2f.cameraPos = camera->transform.position;
planeShader->v2f.dirlights.push_back(light);
//planeShader->v2f.pointlights.push_back(poingt);
planeShader->v2f.v = cameraV;
planeShader->v2f.p = lightP;
planeShader->v2f.lightV = light.LookAt(Vector3f(0, 1, 0));
planeShader->v2f.lightP = lightP;
//ReadObjFile("cube.obj", m);
CreateCube();
CreatePlane();
device->directionlights.push_back(light);
//device->pointlights.push_back(poingt);
Update(w);
system("pause");
return 0;
}
void Update(Window* w)
{
MSG msg = { 0 };
while (msg.message != WM_QUIT)
{
UpdateInput(currentMesh);
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
DoRender(w);
ShowFPS(w);
}
}
}
void DoRender(Window* w)
{
device->Clear(depthBuffer);
device->Clear(zBuffer);
device->DrawMesh(currentMesh, depthShader, depthBuffer);
device->DrawMesh(plane, depthShader, depthBuffer);
device->DrawMesh(currentMesh, phongShader, zBuffer);
device->DrawMesh(plane, planeShader, zBuffer);
//双缓冲
BitBlt(w->hdc, 0, 0, windowWidth, windowHeight, w->screenHDC, 0, 0, SRCCOPY);
}
void ShowFPS(Window* w)
{
static float fps = 0;
static int frameCount = 0;
static float currentTime = 0.0f;
static float lastTime = 0.0f;
frameCount++;
currentTime = timeGetTime() * 0.001f;
if (currentTime - lastTime > 1.0f)
{
fps = (float)frameCount / (currentTime - lastTime);
lastTime = currentTime;
frameCount = 0;
}
char strBuffer[20];
sprintf_s(strBuffer, 20, "%0.3f", fps);
TextOut(w->hdc, 0, 0, strBuffer, 6);
}
void CreateCube()
{
currentMesh->AddVertexData(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, Vector3f(0, 0, -1), Color(1, 0, 0, 1));
currentMesh->AddVertexData(1.0f, -1.0f, -1.0f, 1.0f, 0.0f, Vector3f(0, 0, -1), Color(1, 0, 0, 1));
currentMesh->AddVertexData(1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Vector3f(0, 0, -1), Color(1, 0, 0, 1));
currentMesh->AddVertexData(1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Vector3f(0, 0, -1), Color(1, 0, 0, 1));
currentMesh->AddVertexData(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, Vector3f(0, 0, -1), Color(1, 0, 0, 1));
currentMesh->AddVertexData(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, Vector3f(0, 0, -1), Color(1, 0, 0, 1));
//前
currentMesh->AddVertexData(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, Vector3f(0, 0, 1), Color(0, 1, 0, 1));
currentMesh->AddVertexData(1.0f, -1.0f, 1.0f, 1.0f, 0.0f, Vector3f(0, 0, 1), Color(0, 1, 0, 1));
currentMesh->AddVertexData(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, Vector3f(0, 0, 1), Color(0, 1, 0, 1));
currentMesh->AddVertexData(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, Vector3f(0, 0, 1), Color(0, 1, 0, 1));
currentMesh->AddVertexData(-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, Vector3f(0, 0, 1), Color(0, 1, 0, 1));
currentMesh->AddVertexData(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, Vector3f(0, 0, 1), Color(0, 1, 0, 1));
//左
currentMesh->AddVertexData(-1.0f, 1.0f, 1.0f, 1.0f, 0.0f, Vector3f(-1, 0, 0));
currentMesh->AddVertexData(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Vector3f(-1, 0, 0));
currentMesh->AddVertexData(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f, Vector3f(-1, 0, 0));
currentMesh->AddVertexData(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f, Vector3f(-1, 0, 0));
currentMesh->AddVertexData(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, Vector3f(-1, 0, 0));
currentMesh->AddVertexData(-1.0f, 1.0f, 1.0f, 1.0f, 0.0f, Vector3f(-1, 0, 0));
//右
currentMesh->AddVertexData(1.0f, 1.0f, 1.0f, 1.0f, 0.0f, Vector3f(1, 0, 0), Color(0, 0, 1, 1));
currentMesh->AddVertexData(1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Vector3f(1, 0, 0), Color(0, 0, 1, 1));
currentMesh->AddVertexData(1.0f, -1.0f, -1.0f, 0.0f, 1.0f, Vector3f(1, 0, 0), Color(0, 0, 1, 1));
currentMesh->AddVertexData(1.0f, -1.0f, -1.0f, 0.0f, 1.0f, Vector3f(1, 0, 0), Color(0, 0, 1, 1));
currentMesh->AddVertexData(1.0f, -1.0f, 1.0f, 0.0f, 0.0f, Vector3f(1, 0, 0), Color(0, 0, 1, 1));
currentMesh->AddVertexData(1.0f, 1.0f, 1.0f, 1.0f, 0.0f, Vector3f(1, 0, 0), Color(0, 0, 1, 1));
//下
currentMesh->AddVertexData(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f, Vector3f(0, -1, 0), Color(1, 1, 0, 1));
currentMesh->AddVertexData(1.0f, -1.0f, -1.0f, 1.0f, 1.0f, Vector3f(0, -1, 0), Color(1, 1, 0, 1));
currentMesh->AddVertexData(1.0f, -1.0f, 1.0f, 1.0f, 0.0f, Vector3f(0, -1, 0), Color(1, 1, 0, 1));
currentMesh->AddVertexData(1.0f, -1.0f, 1.0f, 1.0f, 0.0f, Vector3f(0, -1, 0), Color(1, 1, 0, 1));
currentMesh->AddVertexData(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, Vector3f(0, -1, 0), Color(1, 1, 0, 1));
currentMesh->AddVertexData(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f, Vector3f(0, -1, 0), Color(1, 1, 0, 1));
//上
currentMesh->AddVertexData(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, Vector3f(0, 1, 0));
currentMesh->AddVertexData(1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Vector3f(0, 1, 0));
currentMesh->AddVertexData(1.0f, 1.0f, 1.0f, 1.0f, 0.0f, Vector3f(0, 1, 0));
currentMesh->AddVertexData(1.0f, 1.0f, 1.0f, 1.0f, 0.0f, Vector3f(0, 1, 0));
currentMesh->AddVertexData(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, Vector3f(0, 1, 0));
currentMesh->AddVertexData(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, Vector3f(0, 1, 0));
}
void CreatePlane()
{
plane->AddVertexData(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, Vector3f(0, 0, 1), Color(1, 1, 1, 1));
plane->AddVertexData(1.0f, -1.0f, -1.0f, 1.0f, 0.0f, Vector3f(0, 0, 1), Color(1, 1, 1, 1));
plane->AddVertexData(1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Vector3f(0, 0, 1), Color(1, 1, 1, 1));
plane->AddVertexData(1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Vector3f(0, 0, 1), Color(1, 1, 1, 1));
plane->AddVertexData(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, Vector3f(0, 0, 1), Color(1, 1, 1, 1));
plane->AddVertexData(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, Vector3f(0, 0, 1), Color(1, 1, 1, 1));
Matrix M;
Matrix s = m->GetTransform().Scale(Vector3f(5, 5, 5));
Matrix r = m->GetTransform().Rotate(Vector3f(0, 0, 0));
Matrix t = m->GetTransform().Translate(Vector3f(0, 0, -5));
//注意矩阵乘法顺序!!
M = t * r * s;
planeShader->v2f.m = M;
plane->SetObjectToWorld(M);
}