回顾与现在

之前已经在”图片加载”中提及到uv等贴图纹理的核心内容;现在,在状态机中实现纹理类的代码并结合到绘制流程中

代码实现

内容:
1、Texture类编写
2、GPU中的Texture相关状态(生成/删除/绑定/数据)
3、FragmentShader片段着色器加入纹理列表参数
4、示例

base.h

#pragma once

#include<iostream>
#include<vector>
#include<map>
#include<cmath>
#include<assert.h>

#define PI 3.14159265358979323
#define DEG2RAD(theta) (0.01745329251994329 * (theta))
#define FRACTION(v) ((v) - (int)(v))

using byte = unsigned char;

struct RGBA {
byte mB;
byte mG;
byte mR;
byte mA;

RGBA(
byte r = 255,
byte g = 255,
byte b = 255,
byte a = 255)
{
mR = r;
mG = g;
mB = b;
mA = a;
}
};

#define ARRAY_BUFFER 0
#define ELEMENT_ARRAY_BUFFER 1

#define DRAW_LINES 0
#define DRAW_TRIANGLES 1

#define CULL_FACE 1
#define DEPTH_TEST 2
#define BLENDING 3

#define FRONT_FACE 0
#define BACK_FACE 1
#define FRONT_FACE_CW 0
#define FRONT_FACE_CCW 1

#define DEPTH_LESS 0
#define DEPTH_GREATER 1

//纹理参数类型选项
#define TEXTURE_FILTER 0
#define TEXTURE_WRAP_U 1
#define TEXTURE_WRAP_V 2

//纹理参数
#define TEXTURE_FILTER_NEAREST 0 //近似值采样<-------------------------------+
#define TEXTURE_FILTER_LINEAR 1 //双线性插值采样<-------------------------------+

#define TEXTURE_WRAP_REPEAT 0 //重复<-------------------------------+
#define TEXTURE_WRAP_MIRROR 1 //镜像<-------------------------------+

texture.h

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

class Texture {
public:
Texture();
~Texture();

//传入外部图片数据(默认都是rgba格式)
void setBufferData(const uint32_t& width, const uint32_t& height, void* data);

//采样,通过uv坐标返回rgba
math::vec4f getColor(float u, float v);

//设置mFilter与mWrapU/V参数
void setParameter(const uint32_t& type, const uint32_t& value);

private:
void checkWrap(float& n,const uint32_t& type);

private:
RGBA* mBuffer{ nullptr };//在gpu端会储存一段图片内存;在应用端也会存储一段图片内存(符合虚拟gpu的逻辑,将内存上传到gpu端)
uint32_t mWidth{ 0 };
uint32_t mHeight{ 0 };

uint32_t mFilter{ TEXTURE_FILTER_NEAREST };//采样方式(近似值采样或双线性插值采样)
uint32_t mWrapU{ TEXTURE_WRAP_REPEAT };//对于小于0,大于1的U坐标处理方式(重复或镜像)
uint32_t mWrapV{ TEXTURE_WRAP_REPEAT };
};

texture.cpp

#include "texture.h"
#include "../math/math.h"

Texture::Texture() {}
Texture::~Texture() {
if (mBuffer != nullptr) {
delete[] mBuffer;
mBuffer = nullptr;
}
}

void Texture::setBufferData(
const uint32_t& width,
const uint32_t& height,
void* data) {
if (mBuffer != nullptr) {
delete[] mBuffer;
mBuffer = nullptr;
}

mWidth = width;
mHeight = height;

if (mBuffer == nullptr) {
mBuffer = new RGBA[mWidth * mHeight];
}

memcpy(mBuffer, data, mWidth * mHeight * sizeof(RGBA));
}

math::vec4f Texture::getColor(float u, float v) {
RGBA resultColor;

//处理uv坐标
checkWrap(u,mWrapU);
checkWrap(v,mWrapV);

if (mFilter == TEXTURE_FILTER_NEAREST) {
int x = std::round(u * (mWidth - 1));
int y = std::round(v * (mHeight - 1));

int position = y * mWidth + x;
resultColor = mBuffer[position];
}
else if (mFilter == TEXTURE_FILTER_LINEAR) {
float x = u * static_cast<float>(mWidth - 1);
float y = v * static_cast<float>(mHeight - 1);

int left = std::floor(x);
int right = std::ceil(x);
int bottom = std::floor(y);
int top = std::ceil(y);

//对上下插值,得到左右
float yScale = 0.0f;
if (top == bottom) {
yScale = 1.0f;
}
else {
yScale = (y - static_cast<float>(bottom)) / static_cast<float>(top - bottom);
}

int positionLeftTop = top * mWidth + left;
int positionLeftBottom = bottom * mWidth + left;
int positionRightTop = top * mWidth + right;
int positionRightBottom = bottom * mWidth + right;

RGBA leftColor = math::lerp(mBuffer[positionLeftBottom], mBuffer[positionLeftTop], yScale);
RGBA rightColor = math::lerp(mBuffer[positionRightBottom], mBuffer[positionRightTop], yScale);

//对左右插值,得到结果
float xScale = 0.0f;
if (right == left) {
xScale = 1.0f;
}
else {
xScale = (x - static_cast<float>(left)) / static_cast<float>(right - left);
}

resultColor = math::lerp(leftColor, rightColor, xScale);
}

math::vec4f result;
result.x = static_cast<float>(resultColor.mR) / 255.0;
result.y = static_cast<float>(resultColor.mG) / 255.0;
result.z = static_cast<float>(resultColor.mB) / 255.0;
result.w = static_cast<float>(resultColor.mA) / 255.0;

return result;
}

void Texture::checkWrap(float& n, const uint32_t& type) {
if (n > 1.0f || n < 0.0f) {
n = FRACTION(n);

switch (type) {
case TEXTURE_WRAP_REPEAT:
n = FRACTION(n + 1);
break;
case TEXTURE_WRAP_MIRROR:
n = 1.0f - FRACTION(n + 1);
break;
default:
break;
}
}

}

void Texture::setParameter(const uint32_t& type, const uint32_t& value) {
switch (type) {
case TEXTURE_FILTER:
mFilter = value;
break;
case TEXTURE_WRAP_U:
mWrapU = value;
break;
case TEXTURE_WRAP_V:
mWrapV = value;
break;
default:
break;
}
}

gpu.h

class GPU {
//... ... ...
//textures
uint32_t genTexture();
void deleteTexture(const uint32_t& texID);
void bindTexture(const uint32_t& texID);

void texImage2D(const uint32_t& width, const uint32_t& height, void* data);
void texParameter(const uint32_t& param, const uint32_t& value);

void drawElement(const uint32_t& drawMode, const uint32_t& first, const uint32_t& count);
//... ... ...
private:
bool depthTest(const FsOutput& output);

private:
//... ... ...
//texture
uint32_t mCurrentTexture{ 0 };
uint32_t mTextureCounter{ 0 };
std::map<uint32_t, Texture*> mTextureMap;//句柄管理
};

gpu.cpp

uint32_t GPU::genTexture() {
mTextureCounter++;
mTextureMap.insert(std::make_pair(mTextureCounter, new Texture()));

return mTextureCounter;
}

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

mTextureMap.erase(iter);
}

void GPU::bindTexture(const uint32_t& texID) {
mCurrentTexture = texID;
}

void GPU::texImage2D(const uint32_t& width, const uint32_t& height, void* data) {
if (!mCurrentTexture) {
return;
}

auto iter = mTextureMap.find(mCurrentTexture);
if (iter == mTextureMap.end()) {
return;
}
auto texture = iter->second;
texture->setBufferData(width, height, data);
}

void GPU::texParameter(const uint32_t& param, const uint32_t& value) {
if (!mCurrentTexture) {
return;
}

auto iter = mTextureMap.find(mCurrentTexture);
if (iter == mTextureMap.end()) {
return;
}
auto texture = iter->second;
texture->setParameter(param, value);
}

在FragmentShader中加入纹理列表参数

shader.h

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

class Shader {
public:
Shader() {}
~Shader() {}
virtual VsOutput vertexShader(
//VAO当中的bindingMap
const std::map<uint32_t, BindingDescription>& bindingMap,

//VBO当中的bindingMap
const std::map<uint32_t,BufferObject*>& bufferMap,

//当前要处理的顶点的index
const uint32_t& index
) = 0;

virtual void fragmentShader(const VsOutput& input, FsOutput& output, const std::map<uint32_t, Texture*>& textures) = 0;//添加参数textures,在fragmentShader中通过id找到对应的纹理数据<-------------------------------+

//tool functions
public:
//你要先看在VertexShader函数中如何使用,再看逻辑
math::vec4f getVector(
const std::map<uint32_t, BindingDescription>& bindingMap,
const std::map<uint32_t, BufferObject*>& bufferMap,
const uint32_t& attributeLocation,//当前属性的编号
const uint32_t& index);//当前顶点编号

RGBA vectorToRGBA(const math::vec4f& v) {
RGBA color;
color.mR = v.x * 255.0;
color.mG = v.y * 255.0;
color.mB = v.z * 255.0;
color.mA = v.w * 255.0;

return color;
}
};

textureShader.h

#pragma once
#include "../shader.h"
#include "../../math/math.h"

class TextureShader :public Shader {
public:
TextureShader();
~TextureShader();

VsOutput vertexShader(
const std::map<uint32_t, BindingDescription>& bindingMap,
const std::map<uint32_t, BufferObject*>& bufferMap,
const uint32_t& index
)override;

void fragmentShader(const VsOutput& input, FsOutput& output, const std::map<uint32_t, Texture*>& textures)override;

public:
//uniforms
math::mat4f mModelMatrix;
math::mat4f mViewMatrix;
math::mat4f mProjectionMatrix;

uint32_t mDiffuseTexture{ 0 };//与光照相关
};

textureShader.cpp

#include "textureShader.h"

TextureShader::TextureShader() {}
TextureShader::~TextureShader() {}

VsOutput TextureShader::vertexShader(
const std::map<uint32_t, BindingDescription>& bindingMap,
const std::map<uint32_t, BufferObject*>& bufferMap,
const uint32_t& index
) {
VsOutput output;

//取出Attribute数值
math::vec4f position = getVector(bindingMap, bufferMap, 0, index);

//变化为齐次坐标
position.w = 1.0f;
math::vec4f color = getVector(bindingMap, bufferMap, 1, index);

math::vec2f uv = getVector(bindingMap, bufferMap, 2, index);

output.mPosition = mProjectionMatrix * mViewMatrix * mModelMatrix * position;
output.mColor = color;
output.mUV = uv;

return output;
}

void TextureShader::fragmentShader(const VsOutput& input, FsOutput& output, const std::map<uint32_t, Texture*>& textures) {
output.mPixelPos.x = static_cast<int>(input.mPosition.x);
output.mPixelPos.y = static_cast<int>(input.mPosition.y);
output.mDepth = input.mPosition.z;

//取出texture
auto iter = textures.find(mDiffuseTexture);
auto texture = iter->second;

//计算颜色
math::vec4f diffuseColor = texture->getColor(input.mUV.x, input.mUV.y);
output.mColor = vectorToRGBA(diffuseColor);
}

绘制流程中增加纹理参数设置

gpu.cpp

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;

/*
* Clip Space剪裁处理阶段
* 作用:
* 在剪裁空间,对所有输出的图元进行剪裁拼接等
* 后面的处理阶段都使用剪裁结果clipOutputs来处理
*/
std::vector<VsOutput> clipOutputs{};
Clipper::doClipSpace(drawMode, vsOutputs, clipOutputs);
if (clipOutputs.empty()) return;

vsOutputs.clear();

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

/*
* 背面剔除阶段
* 作用:
* 背向我们的三角形需要剔除
*/
std::vector<VsOutput> cullOutputs = clipOutputs;
if (drawMode == DRAW_TRIANGLES && mEnableCullFace) {
cullOutputs.clear();
for (uint32_t i = 0; i < clipOutputs.size() - 2; i += 3) {
if (Clipper::cullFace(mFrontFace, mCullFace, clipOutputs[i], clipOutputs[i + 1], clipOutputs[i + 2])) {
auto start = clipOutputs.begin() + i;
auto end = clipOutputs.begin() + i + 3;
cullOutputs.insert(cullOutputs.end(), start, end);
}
}
}

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

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


if (rasterOutputs.empty()) return;

/*
* 透视恢复阶段
* 作用:
* 离散出来的像素插值结果,需要乘以自身的w值恢复到正常状态
*/
for (auto& output : rasterOutputs) {
perspectiveRecover(output);
}

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

//深度测试
if (mEnableDepthTest && !depthTest(fsOutput)) {
continue;//丢弃未通过深度测试的像素点(被遮挡)
}

RGBA color = fsOutput.mColor;
if (mEnableBlending) {
color = blend(fsOutput);
}

mFrameBuffer->mColorBuffer[pixelPos] = color;
}
}

示例(三角形纹理贴图)

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
TextureShader* shader = nullptr;

//使用的texture
uint32_t texture = 0;
Image* image = nullptr;

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

float angle = 0.0f;
float cameraZ = 1.0f;
void transform() {
angle += 0.002f;
//cameraZ -= 0.01f;

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

//视图变换
auto cameraModelMatrix = math::translate(math::mat4f(1.0f), math::vec3f{ 0.0f, 0.0f, cameraZ });
viewMatrix = math::inverse(cameraModelMatrix);
}

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

sgl->clear();
sgl->useProgram(shader);

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

}

void prepare() {
shader = new TextureShader();

//制造纹理
image = Image::createImage("assets/textures/testTexture.jpg");
texture = sgl->genTexture();
sgl->bindTexture(texture);
sgl->texImage2D(image->mWidth, image->mHeight, image->mData);
sgl->texParameter(TEXTURE_FILTER, TEXTURE_FILTER_LINEAR);
sgl->texParameter(TEXTURE_WRAP_U, TEXTURE_WRAP_REPEAT);
sgl->texParameter(TEXTURE_WRAP_V, TEXTURE_WRAP_REPEAT);
sgl->bindTexture(0);

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, cameraZ });
viewMatrix = math::inverse(cameraModelMatrix);

sgl->enable(CULL_FACE);
sgl->frontFace(FRONT_FACE_CCW);
sgl->cullFace(BACK_FACE);

float positions[] = {
-0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f,
0.0f, 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,
1.0f, 0.0f,
0.5f, 1.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);

//position
positionVbo = sgl->genBuffer();
sgl->bindBuffer(ARRAY_BUFFER, positionVbo);
sgl->bufferData(ARRAY_BUFFER, sizeof(float) * 9, positions);
sgl->vertexAttributePointer(0, 3, 3 * sizeof(float), 0);

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

//uv
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;
Image::destroyImage(image);//将应用端的图片数据释放
sgl->deleteTexture(texture);//将GPU端的图片数据释放

return 0;
}