raster.h

将所有函数私有,只开放rasterize函数

#pragma once
#include "../global/base.h"
#include "dataStructures.h"

/*
* class Raster
* 对外提供静态函数接口,传入离散的图元点,返回光栅化后的像素数组
*/
class Raster {
public:
Raster();
~Raster();


static void rasterize(
std::vector<VsOutput>& results,//光栅化之后,所有信息存储在results中(其中每个像素信息都是vsOutput)
const uint32_t& drawMode,//绘制图元: DRAW_LINES、DRAW_TRIANGLES
const std::vector<VsOutput>& inputs//传入顶点信息
);

private:
static void rasterizeLine(
std::vector<VsOutput>& results,
const VsOutput& v0,
const VsOutput& v1
);

static void interpolantLine(const VsOutput& v0, const VsOutput& v1, VsOutput& target);

static void rasterizeTriangle(
std::vector<VsOutput>& results,
const VsOutput& v0,
const VsOutput& v1,
const VsOutput& v2
);

static void interpolantTriangle(const VsOutput& v0, const VsOutput& v1, const VsOutput& v2, VsOutput& p);

};

raster.cpp

#include "raster.h"
#include "../math/math.h"
#include "dataStructures.h"

Raster::Raster() {}

Raster::~Raster() {}

void Raster::rasterize(
std::vector<VsOutput>& results,
const uint32_t& drawMode,
const std::vector<VsOutput>& inputs) {

if (drawMode == DRAW_LINES) {
for (uint32_t i = 0; i < inputs.size(); i += 2) {
rasterizeLine(results, inputs[i], inputs[i + 1]);
}
}

if (drawMode == DRAW_TRIANGLES) {
for (uint32_t i = 0; i < inputs.size(); i += 3) {
rasterizeTriangle(results, inputs[i], inputs[i + 1], inputs[i + 2]);
}
}
}

void Raster::rasterizeLine(
std::vector<VsOutput>& results,
const VsOutput& v0,
const VsOutput& v1) {

VsOutput start = v0;
VsOutput end = v1;

//1 保证x方向是从小到大的
if (start.mPosition.x > end.mPosition.x) {
auto tmp = start;
start = end;
end = tmp;
}

results.push_back(start);

//2 保证y方向也是从小到大,如果需要翻转,必须记录
bool flipY = false;
if (start.mPosition.y > end.mPosition.y) {
start.mPosition.y *= -1.0f;
end.mPosition.y *= -1.0f;
flipY = true;
}

//3 保证斜率在0-1之间,如果需要调整,必须记录
int deltaX = static_cast<int>(end.mPosition.x - start.mPosition.x);
int deltaY = static_cast<int>(end.mPosition.y - start.mPosition.y);

bool swapXY = false;
if (deltaX < deltaY) {
std::swap(start.mPosition.x, start.mPosition.y);
std::swap(end.mPosition.x, end.mPosition.y);
std::swap(deltaX, deltaY);
swapXY = true;
}

//4 brensenham
int currentX = static_cast<int>(start.mPosition.x);
int currentY = static_cast<int>(start.mPosition.y);

int resultX = 0;
int resultY = 0;

VsOutput currentVsOutput;
int p = 2 * deltaY - deltaX;

for (int i = 0; i < deltaX; ++i) {
if (p >= 0) {
currentY += 1;
p -= 2 * deltaX;
}

currentX += 1;
p += 2 * deltaY;

//处理新xy,flip and swap

resultX = currentX;
resultY = currentY;
if (swapXY) {
std::swap(resultX, resultY);
}

if (flipY) {
resultY *= -1;
}

//产生新顶点
currentVsOutput.mPosition.x = resultX;
currentVsOutput.mPosition.y = resultY;

interpolantLine(start, end, currentVsOutput);

results.push_back(currentVsOutput);
}

}

void Raster::interpolantLine(const VsOutput& v0, const VsOutput& v1, VsOutput& target) {
float weight = 1.0f;
if (v1.mPosition.x != v0.mPosition.x) {
//用x做比例
weight = (float)(target.mPosition.x - v0.mPosition.x) / (float)(v1.mPosition.x - v0.mPosition.x);
}else if (v1.mPosition.y != v0.mPosition.y) {
//用y做比例
weight = (float)(target.mPosition.y - v0.mPosition.y) / (float)(v1.mPosition.y - v0.mPosition.y);
}

target.mColor = math::lerp(v0.mColor, v1.mColor, weight);
target.mUV = math::lerp(v0.mUV, v1.mUV, weight);
}

void Raster::rasterizeTriangle(
std::vector<VsOutput>& results,
const VsOutput& v0,
const VsOutput& v1,
const VsOutput& v2) {
int maxX = static_cast<int>(std::max(v0.mPosition.x, std::max(v1.mPosition.x, v2.mPosition.x)));
int minX = static_cast<int>(std::min(v0.mPosition.x, std::min(v1.mPosition.x, v2.mPosition.x)));
int maxY = static_cast<int>(std::max(v0.mPosition.y, std::max(v1.mPosition.y, v2.mPosition.y)));
int minY = static_cast<int>(std::min(v0.mPosition.y, std::min(v1.mPosition.y, v2.mPosition.y)));

math::vec2f pv0, pv1, pv2;
VsOutput result;
for (int i = minX; i <= maxX; ++i) {
for (int j = minY; j <= maxY; ++j) {
pv0 = math::vec2f(v0.mPosition.x - i, v0.mPosition.y - j);
pv1 = math::vec2f(v1.mPosition.x - i, v1.mPosition.y - j);
pv2 = math::vec2f(v2.mPosition.x - i, v2.mPosition.y - j);

auto cross1 = math::cross(pv0, pv1);
auto cross2 = math::cross(pv1, pv2);
auto cross3 = math::cross(pv2, pv0);

bool negativeAll = cross1 <= 0 && cross2 <= 0 && cross3 <= 0;
bool positiveAll = cross1 >= 0 && cross2 >= 0 && cross3 >= 0;

if (negativeAll || positiveAll) {
result.mPosition.x = i;
result.mPosition.y = j;
interpolantTriangle(v0, v1, v2, result);

results.push_back(result);
}
}
}
}

void Raster::interpolantTriangle(const VsOutput& v0, const VsOutput& v1, const VsOutput& v2, VsOutput& p) {
auto e1 = math::vec2f(v1.mPosition.x - v0.mPosition.x, v1.mPosition.y - v0.mPosition.y);
auto e2 = math::vec2f(v2.mPosition.x - v0.mPosition.x, v2.mPosition.y - v0.mPosition.y);
float sumArea = std::abs(math::cross(e1, e2));

auto pv0 = math::vec2f(v0.mPosition.x - p.mPosition.x, v0.mPosition.y - p.mPosition.y);
auto pv1 = math::vec2f(v1.mPosition.x - p.mPosition.x, v1.mPosition.y - p.mPosition.y);
auto pv2 = math::vec2f(v2.mPosition.x - p.mPosition.x, v2.mPosition.y - p.mPosition.y);
//计算v0的权重

float v0Area = std::abs(math::cross(pv1, pv2));
float v1Area = std::abs(math::cross(pv0, pv2));
float v2Area = std::abs(math::cross(pv0, pv1));

float weight0 = v0Area / sumArea;
float weight1 = v1Area / sumArea;
float weight2 = v2Area / sumArea;

//对于颜色的插值
p.mColor = math::lerp(v0.mColor, v1.mColor, v2.mColor, weight0, weight1, weight2);

//对于uv坐标的插值
p.mUV = math::lerp(v0.mUV, v1.mUV, v2.mUV, weight0, weight1, weight2);
}

gpu.h

1.添加接口: useProgram与drawElement

useProgram:现在gpu是一个状态机,现在要进行的绘制使用的是哪个shader来进行顶点和片元的处理需要外界告知
drawElement:绘制流程

2.新增三个私有函数:vertexShaderStage、perspectiveDivision、screenMapping

vertexShaderStage: 将执行vertexShader的过程独立出来的函数
perspectiveDivision:透视除法(除以w)
screenMapping:传入ndc坐标下的点,处理之后得到屏幕空间的坐标

在调用initSurface时,给mScreenMatrix赋值

#pragma once
#include "../global/base.h"
#include "frameBuffer.h"
#include "../application/application.h"
#include "../math/math.h"
#include "dataStructures.h"
#include "bufferObject.h"
#include "vao.h"
#include "shader.h"

#define sgl GPU::getInstance()

/*
* class GPU:
* 模拟GPU的绘图行为以及算法等
*/
class GPU {
public:
static GPU* getInstance();
GPU();

~GPU();

//接受外界传入的bmp对应的内存指针以及窗体的宽/高; 给mScreenMatrix赋值
void initSurface(const uint32_t& width, const uint32_t& height, void* buffer = nullptr);

//清除画布内容
void clear();

//打印状态机
void printVAO(const uint32_t& vaoID);

uint32_t genBuffer();
void deleteBuffer(const uint32_t& bufferID);
void bindBuffer(const uint32_t& bufferType, const uint32_t& bufferID);
void bufferData(const uint32_t& bufferType, size_t dataSize, void* data);

uint32_t genVertexArray();
void deleteVertexArray(const uint32_t& vaoID);
void bindVertexArray(const uint32_t& vaoID);
void vertexAttributePointer(
const uint32_t& binding,
const uint32_t& itemSize,
const uint32_t& stride,
const uint32_t& offset);

void useProgram(Shader* shader);

void drawElement(const uint32_t& drawMode, const uint32_t& first, const uint32_t& count);

private:
void vertexShaderStage(
std::vector<VsOutput>& vsOutputs,
const VertexArrayObject* vao,
const BufferObject* ebo,
const uint32_t first,
const uint32_t count);//将执行vertexShader的过程独立出来的函数

void perspectiveDivision(VsOutput& vsOutput);//透视除法(除以w)
void screenMapping(VsOutput& vsOutput);//传入ndc坐标下的点,处理之后得到屏幕空间的坐标

private:
static GPU* mInstance;
FrameBuffer* mFrameBuffer{ nullptr };

//VBO相关/EBO也存在内部
uint32_t mCurrentVBO{ 0 };
uint32_t mCurrentEBO{ 0 };
uint32_t mBufferCounter{ 0 };
std::map<uint32_t, BufferObject*> mBufferMap;

//VAO相关
uint32_t mCurrentVAO{ 0 };
uint32_t mVaoCounter{ 0 };
std::map<uint32_t, VertexArrayObject*> mVaoMap;

Shader* mShader{ nullptr };
math::mat4f mScreenMatrix;
};

gpu.cpp

#include "gpu.h"
#include "raster.h"

GPU* GPU::mInstance = nullptr;
GPU* GPU::getInstance() {
if (!mInstance) {
mInstance = new GPU();
}

return mInstance;
}

GPU::GPU() {}

GPU::~GPU() {
if (mFrameBuffer) {
delete mFrameBuffer;
}

for (auto iter : mBufferMap) {
delete iter.second;
}
mBufferMap.clear();

for (auto iter : mVaoMap) {
delete iter.second;
}
mVaoMap.clear();
}

void GPU::initSurface(const uint32_t& width, const uint32_t& height, void* buffer) {
mFrameBuffer = new FrameBuffer(width, height, buffer);
mScreenMatrix = math::screenMatrix<float>(width - 1, height - 1);
}

void GPU::clear() {
size_t pixelSize = mFrameBuffer->mWidth * mFrameBuffer->mHeight;
std::fill_n(mFrameBuffer->mColorBuffer, pixelSize, RGBA(0, 0, 0, 0));
}

void GPU::printVAO(const uint32_t& vaoID) {
auto iter = mVaoMap.find(vaoID);
if (iter != mVaoMap.end()) {
iter->second->print();
}
}

uint32_t GPU::genBuffer() {
mBufferCounter++;
mBufferMap.insert(std::make_pair(mBufferCounter, new BufferObject()));

return mBufferCounter;
}

void GPU::deleteBuffer(const uint32_t& bufferID) {
auto iter = mBufferMap.find(bufferID);
if (iter != mBufferMap.end()) {
delete iter->second;
}
else {
return;
}

mBufferMap.erase(iter);
}

void GPU::bindBuffer(const uint32_t& bufferType, const uint32_t& bufferID) {
if (bufferType == ARRAY_BUFFER) {
mCurrentVBO = bufferID;
}
else if (bufferType == ELEMENT_ARRAY_BUFFER) {
mCurrentEBO = bufferID;
}
}

void GPU::bufferData(const uint32_t& bufferType, size_t dataSize, void* data) {
uint32_t bufferID = 0;
if (bufferType == ARRAY_BUFFER) {
bufferID = mCurrentVBO;
}
else if (bufferType == ELEMENT_ARRAY_BUFFER) {
bufferID = mCurrentEBO;
}
else {
assert(false);
}

auto iter = mBufferMap.find(bufferID);
if (iter == mBufferMap.end()) {
assert(false);
}

BufferObject* bufferObject = iter->second;
bufferObject->setBufferData(dataSize, data);
}

uint32_t GPU::genVertexArray() {
mVaoCounter++;
mVaoMap.insert(std::make_pair(mVaoCounter, new VertexArrayObject()));

return mVaoCounter;
}

void GPU::deleteVertexArray(const uint32_t& vaoID) {
auto iter = mVaoMap.find(vaoID);
if (iter != mVaoMap.end()) {
delete iter->second;
}
else {
return;
}

mVaoMap.erase(iter);
}

void GPU::bindVertexArray(const uint32_t& vaoID) {
mCurrentVAO = vaoID;
}

void GPU::vertexAttributePointer(
const uint32_t& binding,
const uint32_t& itemSize,
const uint32_t& stride,
const uint32_t& offset)
{
auto iter = mVaoMap.find(mCurrentVAO);
if (iter == mVaoMap.end()) {
assert(false);
}

auto vao = iter->second;
vao->set(binding, mCurrentVBO, itemSize, stride, offset);
}

void GPU::useProgram(Shader* shader) {
mShader = shader;
}

void GPU::drawElement(const uint32_t& drawMode, const uint32_t& first, const uint32_t& count) {
if (mCurrentVAO == 0 || mShader == nullptr || count == 0) {
return;
}

//1 get vao
auto vaoIter = mVaoMap.find(mCurrentVAO);
if (vaoIter == mVaoMap.end()) {
std::cerr << "Error: current vao is invalid!" << std::endl;
return;
}

const VertexArrayObject* vao = vaoIter->second;
auto bindingMap = vao->getBindingMap();

//2 get ebo
auto eboIter = mBufferMap.find(mCurrentEBO);
if (eboIter == mBufferMap.end()) {
std::cerr << "Error: current ebo is invalid!" << std::endl;
return;
}

const BufferObject* ebo = eboIter->second;

/*
* VertexShader处理阶段
* 作用:
* 按照输入的EBO的index顺序来处理顶点,
* 依次通过vsShader得到的输出结果按序放入vsOutputs中
*/
std::vector<VsOutput> vsOutputs{};
vertexShaderStage(vsOutputs, vao, ebo, first, count);

if (vsOutputs.empty()) return;

/*
* NDC处理阶段
* 作用:
* 将顶点转化到NDC下
*/
for (auto& output : vsOutputs) {
perspectiveDivision(output);//透视除法,除以W
}

/*
* 屏幕映射处理阶段
* 作用:
* 将NDC下的点通过screenMatrix,转换到屏幕空间
*/
for (auto& output : vsOutputs) {
screenMapping(output);
}

/*
* 光栅化处理阶段
* 作用:
* 离散出所有需要的Fragment
*/
std::vector<VsOutput> rasterOutputs;
Raster::rasterize(rasterOutputs, drawMode, vsOutputs);


if (rasterOutputs.empty()) return;

/*
* 颜色输出处理阶段
* 作用:
* 将颜色进行输出
*/
FsOutput fsOutput;
uint32_t pixelPos = 0;
for (uint32_t i = 0; i < rasterOutputs.size(); ++i) {
mShader->fragmentShader(rasterOutputs[i], fsOutput);
pixelPos = fsOutput.mPixelPos.y * mFrameBuffer->mWidth + fsOutput.mPixelPos.x;
mFrameBuffer->mColorBuffer[pixelPos] = fsOutput.mColor;
}
}

void GPU::vertexShaderStage(
std::vector<VsOutput>& vsOutputs,
const VertexArrayObject* vao,
const BufferObject* ebo,
const uint32_t first,//第一个index
const uint32_t count) {
auto bindingMap = vao->getBindingMap();
byte* indicesData = ebo->getBuffer();

uint32_t index = 0;
for (uint32_t i = first; i < first + count; ++i) {//从first开始拿first + count个顶点索引
// 获取EBO中的第i个index
size_t indicesOffset = i * sizeof(uint32_t);
memcpy(&index, indicesData + indicesOffset, sizeof(uint32_t));

VsOutput output = mShader->vertexShader(bindingMap, mBufferMap, index);
vsOutputs.push_back(output);//得到每一个经过处理后的顶点
}
}

void GPU::perspectiveDivision(VsOutput& vsOutput) {
float oneOverW = 1.0f / vsOutput.mPosition.w;

vsOutput.mPosition *= oneOverW;
vsOutput.mPosition.w = 1.0f;
}

void GPU::screenMapping(VsOutput& vsOutput) {
vsOutput.mPosition = mScreenMatrix * vsOutput.mPosition;
}

示例

uint32_t WIDTH = 800;
uint32_t HEIGHT = 600;

//三个属性对应vbo
uint32_t positionVbo = 0;
uint32_t colorVbo = 0;
uint32_t uvVbo = 0;

//三角形的indices
uint32_t ebo = 0;

//本三角形专属vao
uint32_t vao = 0;

//使用的Shader
DefaultShader* shader = nullptr;

//mvp变换矩阵
math::mat4f modelMatrix;
math::mat4f viewMatrix;
math::mat4f perspectiveMatrix;

float angle = 0.0f;
void transform() {
angle += 0.01f;
//模型变换
modelMatrix = math::rotate(math::mat4f(1.0f), angle, math::vec3f{ 0.0f, 1.0f, 0.0f });
}

void render() {
transform();
shader->mModelMatrix = modelMatrix;
shader->mViewMatrix = viewMatrix;
shader->mProjectionMatrix = perspectiveMatrix;

sgl->clear();
sgl->useProgram(shader);
sgl->bindVertexArray(vao);
sgl->bindBuffer(ELEMENT_ARRAY_BUFFER, ebo);
sgl->drawElement(DRAW_TRIANGLES, 0, 3);
}

void prepare() {
shader = new DefaultShader();//生成一个defaultShader

perspectiveMatrix = math::perspective(60.0f, (float)WIDTH / (float)HEIGHT, 0.1f, 100.0f);

auto cameraModelMatrix = math::translate(math::mat4f(1.0f), math::vec3f{ 0.0f, 0.0f, 3.0f });
viewMatrix = math::inverse(cameraModelMatrix);

float positions[] = {
-0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};

float colors[] = {
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
};

float uvs[] = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
};

uint32_t indices[] = { 0, 1, 2 };

//生成indices对应ebo
ebo = sgl->genBuffer();
sgl->bindBuffer(ELEMENT_ARRAY_BUFFER, ebo);
sgl->bufferData(ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * 3, indices);
sgl->bindBuffer(ELEMENT_ARRAY_BUFFER, 0);

//生成vao并且绑定
vao = sgl->genVertexArray();
sgl->bindVertexArray(vao);

//生成每个vbo,绑定后,设置属性ID及读取参数
auto positionVbo = sgl->genBuffer();
sgl->bindBuffer(ARRAY_BUFFER, positionVbo);
sgl->bufferData(ARRAY_BUFFER, sizeof(float) * 9, positions);
sgl->vertexAttributePointer(0, 3, 3 * sizeof(float), 0);

auto colorVbo = sgl->genBuffer();
sgl->bindBuffer(ARRAY_BUFFER, colorVbo);
sgl->bufferData(ARRAY_BUFFER, sizeof(float) * 12, colors);
sgl->vertexAttributePointer(1, 4, 4 * sizeof(float), 0);

auto uvVbo = sgl->genBuffer();
sgl->bindBuffer(ARRAY_BUFFER, uvVbo);
sgl->bufferData(ARRAY_BUFFER, sizeof(float) * 6, uvs);
sgl->vertexAttributePointer(2, 2, 2 * sizeof(float), 0);

sgl->bindBuffer(ARRAY_BUFFER, 0);
sgl->bindVertexArray(0);
}

int APIENTRY wWinMain(
_In_ HINSTANCE hInstance, //本应用程序实例句柄,唯一指代当前程序
_In_opt_ HINSTANCE hPrevInstance, //本程序前一个实例,一般是null
_In_ LPWSTR lpCmdLine, //应用程序运行参数
_In_ int nCmdShow) //窗口如何显示(最大化、最小化、隐藏),不需理会
{
if (!app->initApplication(hInstance, WIDTH, HEIGHT)) {
return -1;
}

//将bmp指向的内存配置到sgl当中
sgl->initSurface(app->getWidth(), app->getHeight(), app->getCanvas());

prepare();

bool alive = true;
while (alive) {
alive = app->peekMessage();
render();
app->show();
}

delete shader;

return 0;
}