学完games101课程的人都会想手写一个光栅化渲染器,本系列文章将介绍如何手写一个光栅化渲染器。
ps:作者也是只学了games101,边学边写的状态难免写的不好。

大致流程如下:

  1. 渲染器

    • 创建图片文件
    • 像素缓冲区(存放图片像素信息)
    • 设置指定位置像素信息(setPixel函数)
    • 保存图片(将像素缓冲区写入图片格式)
  2. 光栅化器

    1. 光栅化直线
    2. 光栅化圆形
    3. 光栅化三角形
    4. 顶点数组对象
    5. 顶点缓冲区
    6. 元素缓冲区
    7. 顶点布局设置
    8. 顶点着色器(各种图形变换)
    9. z-buffer深度缓冲
    10. MSAA抗锯齿
    11. 片段着色器
    12. 纹理贴图
    13. 模型加载

创建一个渲染器

创建文件图片可以使用读写二进制文件格式的方式创建图片,可以使用Bmp文件格式,这个文件格式好创建。
有兴趣的可以看这篇文章
或者看看我之前写废的BMP光栅化渲染器,当然是借鉴games101作业写的。
这里就使用png图片格式了,这里使用vspng这个简单的png文件库,该库只有短短的30几行的代码。
创建项目目录

├── CMakeLists.txt
├── build
├── include
│   ├── Rasterizer.hpp
│   └── svpng
│       ├── svpng.h
└── src
    ├── main.cpp
    └── svpng
        └── svpng.cpp

将svpng中的svpng函数分文件编写不然会触发重复定义的错误
CMakeLists.txt内容如下

cmake_minimum_required(VERSION 3.10.0)

project(Rasterization)

include_directories("./include/svpng/")
include_directories("./include/")
file(GLOB_RECURSE CPP_FILE "${PROJECT_SOURCE_DIR}/src/*.cpp")
file(GLOB_RECURSE SVPNG_FILE "${PROJECT_SOURCE_DIR}/src/svpng/*.cpp")

add_executable(${PROJECT_NAME} ${CPP_FILE} ${SVPNG_FILE})

vspng.cpp内容如下

#include "svpng.h"
SVPNG_LINKAGE void svpng(SVPNG_OUTPUT, unsigned w, unsigned h, const unsigned char* img, int alpha) {
    static const unsigned t[] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 
    /* CRC32 Table */    0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
    unsigned a = 1, b = 0, c, p = w * (alpha ? 4 : 3) + 1, x, y, i;   /* ADLER-a, ADLER-b, CRC, pitch */
#define SVPNG_U8A(ua, l) for (i = 0; i < l; i++) SVPNG_PUT((ua)[i]);
#define SVPNG_U32(u) do { SVPNG_PUT((u) >> 24); SVPNG_PUT(((u) >> 16) & 255); SVPNG_PUT(((u) >> 8) & 255); SVPNG_PUT((u) & 255); } while(0)
#define SVPNG_U8C(u) do { SVPNG_PUT(u); c ^= (u); c = (c >> 4) ^ t[c & 15]; c = (c >> 4) ^ t[c & 15]; } while(0)
#define SVPNG_U8AC(ua, l) for (i = 0; i < l; i++) SVPNG_U8C((ua)[i])
#define SVPNG_U16LC(u) do { SVPNG_U8C((u) & 255); SVPNG_U8C(((u) >> 8) & 255); } while(0)
#define SVPNG_U32C(u) do { SVPNG_U8C((u) >> 24); SVPNG_U8C(((u) >> 16) & 255); SVPNG_U8C(((u) >> 8) & 255); SVPNG_U8C((u) & 255); } while(0)
#define SVPNG_U8ADLER(u) do { SVPNG_U8C(u); a = (a + (u)) % 65521; b = (b + a) % 65521; } while(0)
#define SVPNG_BEGIN(s, l) do { SVPNG_U32(l); c = ~0U; SVPNG_U8AC(s, 4); } while(0)
#define SVPNG_END() SVPNG_U32(~c)
    SVPNG_U8A("\x89PNG\r\n\32\n", 8);           /* Magic */
    SVPNG_BEGIN("IHDR", 13);                    /* IHDR chunk { */
    SVPNG_U32C(w); SVPNG_U32C(h);               /*   Width & Height (8 bytes) */
    SVPNG_U8C(8); SVPNG_U8C(alpha ? 6 : 2);     /*   Depth=8, Color=True color with/without alpha (2 bytes) */
    SVPNG_U8AC("\0\0\0", 3);                    /*   Compression=Deflate, Filter=No, Interlace=No (3 bytes) */
    SVPNG_END();                                /* } */
    SVPNG_BEGIN("IDAT", 2 + h * (5 + p) + 4);   /* IDAT chunk { */
    SVPNG_U8AC("\x78\1", 2);                    /*   Deflate block begin (2 bytes) */
    for (y = 0; y < h; y++) {                   /*   Each horizontal line makes a block for simplicity */
        SVPNG_U8C(y == h - 1);                  /*   1 for the last block, 0 for others (1 byte) */
        SVPNG_U16LC(p); SVPNG_U16LC(~p);        /*   Size of block in little endian and its 1's complement (4 bytes) */
        SVPNG_U8ADLER(0);                       /*   No filter prefix (1 byte) */
        for (x = 0; x < p - 1; x++, img++)
            SVPNG_U8ADLER(*img);                /*   Image pixel data */
    }
    SVPNG_U32C((b << 16) | a);                  /*   Deflate block end with adler (4 bytes) */
    SVPNG_END();                                /* } */
    SVPNG_BEGIN("IEND", 0); SVPNG_END();        /* IEND chunk {} */
}

vspng.h内容如下,注释太多已去除


#ifndef SVPNG_INC_
#define SVPNG_INC_


#ifndef SVPNG_LINKAGE
#define SVPNG_LINKAGE
#endif

#ifndef SVPNG_OUTPUT
#include <stdio.h>
#define SVPNG_OUTPUT FILE* fp
#endif

#ifndef SVPNG_PUT
#define SVPNG_PUT(u) fputc(u, fp)
#endif

SVPNG_LINKAGE void svpng(SVPNG_OUTPUT, unsigned w, unsigned h, const unsigned char* img, int alpha);

#endif /* SVPNG_INC_ */

然后创建Render.hpp 和 Render.cpp

#ifndef RENDER_HPP
#define RENDER_HPP
#define OPEN_ARGB true
#define CLOSE_ARGB false
#include "svpng.h"
class Render{
private:
    unsigned int m_width,m_height;
    unsigned char* m_buffer;
    FILE* m_fp;
    bool m_rgba_state = CLOSE_ARGB;
    unsigned int m_size = 3;
public:
    Render();
    Render(const unsigned int w,const unsigned int h,
            bool rgba = CLOSE_ARGB);
    ~Render();
    unsigned int getWidth();
    unsigned int getHeight();
    void setPixel(const unsigned int x,
                  const unsigned int y,
                  const unsigned int r,
                  const unsigned int g,
                  const unsigned int b,
                  const unsigned int a=0);
    bool createFile(const char* path);
    bool saveFile();
};
#endif
#include "Render.hpp"
#include <cstdio>

Render::Render()
{
}

Render::Render(
    const unsigned int w,
    const unsigned int h,
    bool rgba)
    : m_width(w)
    , m_height(h)
    , m_rgba_state(rgba)
{
    if (m_rgba_state)
        m_size = 4;
    m_buffer = new unsigned char[w * h * m_size]();
}
Render::~Render()
{
    delete[] m_buffer;
}
bool Render::createFile(const char* path)
{
    m_fp = fopen(path, "wb");
    if (m_fp == nullptr)
        return false;
    else
        return true;
}

void Render::setPixel(
    const unsigned int x,
    const unsigned int y,
    const unsigned int r,
    const unsigned int g,
    const unsigned int b,
    const unsigned int a)
{
    m_buffer[(y * m_width + x) * m_size + 0] = r;
    m_buffer[(y * m_width + x) * m_size + 1] = g;
    m_buffer[(y * m_width + x) * m_size + 2] = b;
    if (m_rgba_state)
        m_buffer[(y * m_width + x) * m_size + 3] = a;
}
bool Render::saveFile()
{
    svpng(m_fp, m_width, m_height, m_buffer, m_rgba_state);
    return fclose(m_fp);
}

unsigned int Render::getWidth(){return m_width;}
unsigned int Render::getHeight(){return m_height;}

然后就大功告成了

最后修改:2025 年 03 月 09 日
如果觉得我的文章对你有用,请随意赞赏