由于本文章是对TinyRenderer的模仿,所以并不打算引入外部库。

那么我们第一步需要解决的就是图形输出的问题,毕竟,如果连渲染的结果都看不到,那还叫什么Renderer嘛。

由于不引入外部库,所以选择输出的图片格式应该越简单越好,各种位图就成为了我们的首选。

这里我们选择了生态较好的bmp位图。

技术上,由于只使用C++,所以各种文件流就成了我们构建图片的唯一工具。

本章目标

输出一张保存了我们渲染结果的bmp位图

需求:

  • 大小可以控制,也就是位图的尺寸可控
  • 控制某个像素点的颜色,精准更改set()
  • 对位图进行上下反转

实现

BMPImage.h

#ifndef BMP_IMAGE_H
#define BMP_IMAGE_H
#include <string>
#include <vector> #pragma pack(push, 1)
struct BMPFileHeader
{
uint16_t bfType; // BMP文件的类型,必须为"B"然后是"M"
uint32_t bfSize; // 文件大小
uint16_t bfReserved1; // 保留字,必须为0
uint16_t bfReserved2; // 从文件头到实际位图数据的偏移字节数
uint32_t bfOffBits; // 信息头的大小
}; struct BMPInfoHeader
{
uint32_t biSize; // info head size
int32_t biWidth; // 图像宽度
int32_t biHeight; // 图像高度
uint16_t biPlanes; // 图像的位面数
uint16_t biBitCount; // 每个像素的位数
uint32_t biCompression; // 压缩类型
uint32_t biSizeImage; // 图像的大小,以字节为单位
int32_t biXPelsPerMeter; // 水平分辨率
int32_t biYPelsPerMeter; // 垂直分辨率
uint32_t biClrUsed; // 位图实际使用的颜色表中的颜色数
uint32_t biClrImportant; // 位图显示过程中重要的颜色数
};
#pragma pack(pop) /**
* \brief custom the color format used
*/
enum ColorFormat
{
RGB,
CMYK
}; struct RGBPixel
{
uint8_t red;
uint8_t green;
uint8_t blue;
RGBPixel() : red(0), green(0), blue(0)
{
}
RGBPixel(uint8_t red, uint8_t green, uint8_t blue) : red(red), green(green), blue(blue)
{
}
}; class BMPImage
{
public:
BMPImage() = delete;
BMPImage(unsigned int width, unsigned int height, ColorFormat colorFormat = ColorFormat::RGB);
void loadData(std::vector<char>&& userData);
void generate(const std::string& fileName);
void loadDataAndGenerate(std::vector<char>&& userData, const std::string& fileName); void set(int x, int y, RGBPixel pixel);
void flipVertically(); private:
BMPFileHeader fileHeader;
BMPInfoHeader infoHeader; ColorFormat colorFormat;
std::vector<unsigned char> pixelData;
}; #endif

Important:

  • 在组织bmp文件头的部分,一定要使用预处理宏#pragma pack(push, 1)#pragma pack(pop),控制内存对齐方式为单字节,否则会由于编译器控制的内存对齐而导致文件格式错误,从而不能正确输出

BMPImage.cpp

#include "TinyRenderer/BMPImage.h"

#include <fstream>
#include <iostream> BMPImage::BMPImage(unsigned width, unsigned height, ColorFormat colorFormat)
{
int rowSize = (width * 3 + 3) & (~3); // Ensure row size is a multiple of 4 bytes
int fileSize = rowSize * height + sizeof(BMPFileHeader) + sizeof(BMPInfoHeader); // Set BMP file header
fileHeader.bfType = 0x4D42; // 'BM'
fileHeader.bfSize = fileSize;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader); // Set BMP info header
infoHeader.biSize = sizeof(BMPInfoHeader);
infoHeader.biWidth = width;
infoHeader.biHeight = height;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;
infoHeader.biCompression = 0;
infoHeader.biSizeImage = rowSize * height;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0; // Initialize pixel data
pixelData.resize(rowSize * height, 0);
} // not important now
void BMPImage::loadData(std::vector<char>&& userData)
{
// TODO: load image
} void BMPImage::generate(const std::string& fileName)
{
std::ofstream file(fileName, std::ios::out | std::ios::binary);
if (!file)
{
std::cerr << "Error: Unable to open file for writing." << std::endl;
return;
} // Write headers
file.write(reinterpret_cast<const char*>(&fileHeader), sizeof(fileHeader));
file.write(reinterpret_cast<const char*>(&infoHeader), sizeof(infoHeader)); // Write pixel data
file.write(reinterpret_cast<const char*>(pixelData.data()), pixelData.size()); file.close();
} void BMPImage::loadDataAndGenerate(std::vector<char>&& userData, const std::string& fileName)
{
} void BMPImage::set(int x, int y, RGBPixel pixel)
{
if (x < 0 || y < 0 || x >= infoHeader.biWidth || y >= infoHeader.biHeight)
{
throw std::out_of_range("Pixel coordinates are out of bounds");
}
int rowSize = (infoHeader.biWidth * 3 + 3) & (~3);
int index = (infoHeader.biHeight - 1 - y) * rowSize + x * 3;
pixelData[index] = pixel.blue;
pixelData[index + 1] = pixel.green;
pixelData[index + 2] = pixel.red;
} void BMPImage::flipVertically()
{
int width = infoHeader.biWidth;
int height = infoHeader.biHeight;
int rowSize = (width * 3 + 3) & (~3); for (int y = 0; y < height / 2; ++y)
{
int topIndex = y * rowSize;
int bottomIndex = (height - 1 - y) * rowSize;
for (int x = 0; x < rowSize; ++x)
{
std::swap(pixelData[topIndex + x], pixelData[bottomIndex + x]);
}
}
}

测试

main.cpp

#include "TinyRenderer/TinyRenderer.h"
#include "TinyRenderer/BMPImage.h" int main()
{
BMPImage image(100, 100, ColorFormat::RGB);
RGBPixel white(255, 255, 255);
image.set(22, 77, white);
image.flipVertically();
image.generate("test.bmp");
std::cout << "Image Generated." << std::endl;
return 0;
}

请忽略TinyRenderer/TinyRenderer.h,里面仅是一些头文件。

输出结果

你能看到那个白点吗?那是我们的起点。

Chapter1 p1 Output Image的更多相关文章

  1. sql分页操作

    看到了网上关于分页的讲解  对最快的分页语句做了测试 还别说速度真快 总共6w条数据 速度确实so 快 前提是id是主键 或者是索引 declare @page int;--页数 declare @P ...

  2. C#解析json文件的方法

    C# 解析 json JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的 ...

  3. 设计一个用于人事管理的People(人员)类

    #include <iostream> #include <string> using namespace std; class Date //日期类 { private: i ...

  4. JSON学习

    1.JSON 语法是 JavaScript 对象表示语法的子集. l  数据在名称/值对中 l  数据由逗号分隔 l  花括号保存对象 l  方括号保存数组 JSON 值可以是: l  数字(整数或浮 ...

  5. js的this和面向对象编程

    很奇怪的是很多书或资料没有把这个事情讲清楚. 关键就是在于没有一个整体的思维技术模式,问题被隔离了所以反而不容易理解. 我们先看this,这是js的关键字,指示函数的上下文对象. 这里问题就来了,比如 ...

  6. oracle,mysql,SqlServer三种数据库的分页查询的实例。

    MySql: MySQL数据库实现分页比较简单,提供了 LIMIT函数.一般只需要直接写到sql语句后面就行了.LIMIT子 句可以用来限制由SELECT语句返回过来的数据数量,它有一个或两个参数,如 ...

  7. C# 解析JSON格式数据

    JSON简介 JSON(全称为JavaScript ObjectNotation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集.JSON采用完全独立于语言的文本格式,可 ...

  8. C# 解析 Json数据

    JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的文本格式,可以很容易在 ...

  9. C# 解析 Json

    C# 解析 json JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的 ...

  10. sql server 2000数据库 最近经常出现某进程一直占用资源,阻塞?死锁?

    OA的数据库最近多次出现某进程一直占用资源,导致其他进程无法执行.使用sp_who2 和 sql server profiler跟踪查询,发现有以下几个语句常常占用资源: 1.declare @P1 ...

随机推荐

  1. 力扣383(java&python)-赎金信(简单)

    题目: 给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成. 如果可以,返回 true :否则返回 false . m ...

  2. 五福背后的 Web 3D 引擎 Oasis Engine 正式开源

    简介: Oasis 从开源走向新的起点,用 3D 化的交互和表达让世界变得更美好. 相信大家已经体验了今年支付宝五福的活动,无论是今年的五福首页还是打年兽游戏都是由蚂蚁互动图形引擎(代号:Oasis ...

  3. Fluid — 云原生环境下的高效“数据物流系统”

    简介: 为了解决大数据.AI 等数据密集型应用在云原生计算存储分离场景下,存在的数据访问延时高.联合分析难.多维管理杂等痛点问题,南京大学 PASALab.阿里巴巴.Alluxio 在 2020 年 ...

  4. [JDBC] Kettle on MaxCompute 使用指南

    简介: Kettle是一款开源的ETL工具,纯Java实现,可以在Windows.Unix和Linux上运行,提供图形化的操作界面,可以通过拖拽控件的方式,方便地定义数据传输的拓扑 .基本讲介绍基于K ...

  5. 鸿蒙HarmonyOS实战-ArkUI事件(触屏事件)

    前言 触屏事件是指通过触摸屏幕来进行操作和交互的事件.常见的触屏事件包括点击(tap).双击(double tap).长按(long press).滑动(swipe).拖动(drag)等.触屏事件通常 ...

  6. rpc 和 http的区别

  7. ansible系列(22)--ansible的Facts Variables

    目录 1 Ansible Facts Variables 1.1 facts的获取方法 1.2 根据主机IP地址生成Redis配置文件 1.3 根据主机CPU核数生成Nginx配置 1.4 根据主机内 ...

  8. 【项目学习】Anchor:一种提供稳定币存款低波动收益率的去中心化的储蓄协议

    简介 基于稳定币的获利产品. 贷方人放出稳定币以供借款.借方通过抵押资产(base asset)的方式,以低于协议定义的借贷比率借入稳定币.Anchor 协议使用抵押资产进行质押以获得奖励,然后将质押 ...

  9. 暴力破解(Pikachu)

    暴力破解(Pikachu靶场) Burpsuite4种攻击类型 Sinper(狙击手): 可以理解为一个一个爆破,也就是字典只能设置一个,然后用字典替换选择的参数,爆破完一个参数后再爆破另一个参数. ...

  10. C++ 资源大全:标准库、Web框架、人工智能等 | 最全整理

    C++ 资源列表,内容包括: 标准库.Web应用框架.人工智能.数据库.图片处理.机器学习.日志.代码分析等 目录 进程间通信 Json 日志 机器学习 数学 内存分配 多媒体 网络 PDF 物理学 ...