Chapter1 p1 Output Image

news/2024/10/7 16:18:54

由于本文章是对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; // 保留字,必须为0uint16_t bfReserved2; // 从文件头到实际位图数据的偏移字节数uint32_t bfOffBits;   // 信息头的大小
};struct BMPInfoHeader
{uint32_t biSize;         // info head sizeint32_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 bytesint fileSize = rowSize * height + sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);// Set BMP file headerfileHeader.bfType = 0x4D42; // 'BM'fileHeader.bfSize = fileSize;fileHeader.bfReserved1 = 0;fileHeader.bfReserved2 = 0;fileHeader.bfOffBits = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);// Set BMP info headerinfoHeader.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 datapixelData.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 headersfile.write(reinterpret_cast<const char*>(&fileHeader), sizeof(fileHeader));file.write(reinterpret_cast<const char*>(&infoHeader), sizeof(infoHeader));// Write pixel datafile.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,里面仅是一些头文件。

输出结果

img

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hjln.cn/news/42764.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

吴恩达机器学习第三课 Unsupervised learning recommenders reinforcement learning

Unsupervised learning recommenders reinforcement learning 1.1 课程介绍2.1 什么是聚类

KPTI——可以缓解“熔断” (Meltdown) 漏洞的内核新特性

Linux 内核修复办法:内核页表隔离KPTl(kernel page table isolation)每个进程一张页表变成两张:运行在内核态和运行在用户态时分别使用各自分离的页表Kernel页表包含了进程用户空间地址的映射和Kernel使用的内存映射用户页表仅仅包含了用户空间的内存映射以及内核跳板的内存映射…

吴恩达机器学习第二课 Advanced Learning Algorithms

Advanced Learning Algorithms week1 1.1 神经元和大脑1.2 需求预测构建自己神经网络的时候:需要决定隐藏层的个数和每个隐藏层的神经元个数1.3 图像感知 像素的亮度值从0~255变化 人脸识别:训练一个神经网络,以一个特征向量作为输入,输出图片中人的身份2.1 神经网络中的网…

nanoDLA逻辑分析仪上手教程

逻辑分析仪分析自定义总线数据前言 最近调试NXP FRDM-MCXN947开发板,发现它的硬件i2c接口读取的传感器数据老是不对,排查了硬件电路也发现不了啥问题;于是乎想到用逻辑分析仪试一下,果然很快定位到问题所在;还是那句话,用对的工具做对的事情,别浪费时间!这篇文章主要关…

字符串处理,push pop路径,组合命令

字符串处理字符串截取、命令嵌套命令格式:%变量名:~ m,n%,其中,m表示开始位置(默认开头),n表示从m位置开始向后截取的字符个数(默认到结尾),若n为负数则表示向前截取个数,作用:将命令中的某段字符截取,通过call将字符做为命令执行。 @echo offset str1=aaa echo ok bbb…

V-STARS 摄影测量基础

本文翻译了 GSI 公司官方网站中所提供的“摄影测量基础”文章。V-STARS 摄影测量基础[!NOTE] 本文翻译自 Geodetic Systems Inc,原文链接:https://www.geodetic.com/basics-of-photogrammetry/ 。摄影测量 摄影测量基础知识 在描述V-STARS系统(Video-Simultaneous Triangula…

ARM64中的ASID地址空间标识符

1. 从ARM32到ARM64 从ARM32到ARM64不止将处理器从32位升级到了64位,还有许多性能的技术也得到了极大的提升,光是个头长了可不行啊!能耐也得跟着长啊!哈哈哈 1.1 ARM32的TLB机制如上图所示,上一讲我们讲了TLB的每一条表项都有一个bit用来表示自己是全局的(内核空间)还是本…

[linux]mark-tool

编写shell终端的目录收藏工具前言 在远程连接到linux进行操作时,经常要切换目录,有些目录切换频次较高,因此写了个shell工具,用于收藏目录、切换目录等。也不需要安装,直接添加脚本即可。配置 首先声明脚本是基于bash shell,zsh和fish未经测试。编辑文件~/.bash_custom_f…