tinyrenderer-渲染器着色

整理了代码,创建了一个相机类,控制镜头

class Camera
{
public:
	Camera(Vec3f cameraPos, Vec3f target, Vec3f up):cameraPos_(cameraPos), target_(target), up_(up) {}
	Matrix getView();
	Matrix getProjection();
private:
	Vec3f cameraPos_;
	Vec3f target_;
	Vec3f up_;
};

以及一个专门渲染的Rasterizer类

class Rasterizer
{
public:
	Rasterizer(Matrix viewport, int width, int height);
	void setModelView(Matrix modelView) { modelView_ = modelView; }
	void setProjection(Matrix projection) { projection_ = projection; }
	void draw(Model* model, IShader& shader, TGAImage& image);
	Matrix getProjection() { return projection_; }
	Matrix getModelView() { return modelView_; }
private:
	void triangle(Vec4f* pts, IShader& shader, TGAImage& image);

	Matrix viewport_;
	Matrix modelView_;
	Matrix projection_;
	std::vector<float> zBuffer_;
	int width_;
	int height_;
};

一个着色器结构,分别处理顶点着色,和片段着色

struct IShader
{
	virtual Vec3i vertex(int iface, int vertIdx) = 0;
	virtual bool fragment(Vec3f bar, TGAColor& color) = 0;
};

顶点着色是对模型的三角形顶点进行变换处理
片段是对三角形内部的每个点进行循环插值

void Rasterizer::draw(Model* model, IShader& shader, TGAImage& image) {
    for (int i = 0; i < model->faceSize(); i++) {
        Vec4f screencoords[3];
        for (int j = 0; j < 3; j++) {
            screencoords[j] = viewport_ * shader.vertex(i, j);
        }
        triangle(screencoords, shader, image);
    }
}

void triangle(Vec4f* pts, IShader& shader, TGAImage& image) {
	//...
	for (int x = minX; x < maxX; x++) {
		for (int y = minY; y < maxY; y++) {
			//...
	        bool discard = shader.fragment(Vec3f(alpha, beta, gamma), color);
	        //...
	     }
	}
}

在这里插入图片描述
出现了破面,破面问题基本上就是数据精度的误差,应该是更新了新的geometry.h文件,导致一些值的浮点数和整数的选择出现了新的误差
调整了一下
在这里插入图片描述
试了下教程的片段着色器,并且调整了下光源方向
在这里插入图片描述
flat shading

struct FlatShader :public IShader
{
    Vec3f vert[3];
    virtual Vec4f vertex(int iface, int vertIdx) {

        vert[vertIdx] = model->vert(model->face(iface)[vertIdx]);
        return rasterizer->getProjection() * rasterizer->getModelView() * embed<4>(vert[vertIdx]);
    }
    virtual bool fragment(Vec3f barycentricCoordinates, TGAColor& color) {
        Vec3f n = cross((vert[1] - vert[0]), (vert[2] - vert[0])).normalize();
        float I = std::max(0.f, n * light_dir);
        color = TGAColor(255, 255, 255) * I;
        return false;
    }
};

在这里插入图片描述
Phong shading

struct PhongShader :public IShader
{
    Vec3f n[3];
    virtual Vec4f vertex(int iface, int vertIdx) {
        n[vertIdx] = model->nverts(model->nface(iface)[vertIdx]);
        Vec3f v = model->vert(model->face(iface)[vertIdx]);
        return rasterizer->getProjection() * rasterizer->getModelView() * embed<4>(v);
    }
    virtual bool fragment(Vec3f barycentricCoordinates, TGAColor& color) {
        Vec3f normal = n[0] * barycentricCoordinates.x + n[1] * barycentricCoordinates.y + n[2] * barycentricCoordinates.z;
        float I = std::max(0.f, normal * light_dir);
        color = TGAColor(255, 255, 255) * I;
        return false;
    }
};

在这里插入图片描述
gouraud 纹理着色

struct GouraudTexShader :public IShader
{
    Vec3f intensity;
    mat<2, 3, float> uv;
    virtual Vec4f vertex(int iface, int vertIdx) {
        Vec3f n = model->nverts(model->nface(iface)[vertIdx]);
        intensity[vertIdx] = std::max(0.f, n * light_dir);
        Vec3f v = model->vert(model->face(iface)[vertIdx]);
        uv.set_col(vertIdx, model->tverts(model->tface(iface)[vertIdx]));
        return rasterizer->getProjection() * rasterizer->getModelView() * embed<4>(v);
    }
    virtual bool fragment(Vec3f barycentricCoordinates, TGAColor& color) {
        float I = intensity * barycentricCoordinates;
        Vec2f texcoords = uv * barycentricCoordinates;
        color = model->getDiffuseColor(texcoords.x, texcoords.y) * I;
        return false;
    }
};

在这里插入图片描述
flat纹理着色

struct FlatTexShader :public IShader
{
    Vec3f vert[3];
    mat<2, 3, float> uv;
    virtual Vec4f vertex(int iface, int vertIdx) {
        vert[vertIdx] = model->vert(model->face(iface)[vertIdx]);
        uv.set_col(vertIdx, model->tverts(model->tface(iface)[vertIdx]));
        return rasterizer->getProjection() * rasterizer->getModelView() * embed<4>(vert[vertIdx]);
    }
    virtual bool fragment(Vec3f barycentricCoordinates, TGAColor& color) {
        Vec3f n = cross((vert[1] - vert[0]), (vert[2] - vert[0])).normalize();
        float I = std::max(0.f, n * light_dir);
        Vec2f texcoords = uv * barycentricCoordinates;
        color = model->getDiffuseColor(texcoords.x, texcoords.y) * I;
        return false;
    }
};

在这里插入图片描述
Phong纹理着色

struct PhongTexShader :public IShader
{
    Vec3f n[3];
    mat<2, 3, float> uv;
    virtual Vec4f vertex(int iface, int vertIdx) {
        n[vertIdx] = model->nverts(model->nface(iface)[vertIdx]);
        Vec3f v = model->vert(model->face(iface)[vertIdx]);
        uv.set_col(vertIdx, model->tverts(model->tface(iface)[vertIdx]));
        return rasterizer->getProjection() * rasterizer->getModelView() * embed<4>(v);
    }
    virtual bool fragment(Vec3f barycentricCoordinates, TGAColor& color) {
        Vec3f normal = n[0] * barycentricCoordinates.x + n[1] * barycentricCoordinates.y + n[2] * barycentricCoordinates.z;
        float I = std::max(0.f, normal * light_dir);
        Vec2f texcoords = uv * barycentricCoordinates;
        color = model->getDiffuseColor(texcoords.x, texcoords.y) * I;
        return false;
    }
};

在这里插入图片描述
使用法线贴图shader

struct normalTexShader :public IShader {
    mat<2, 3, float> uv;
    virtual Vec4f vertex(int iface, int vertIdx) {
        Vec3f v = model->vert(model->face(iface)[vertIdx]);
        uv.set_col(vertIdx, model->tverts(model->tface(iface)[vertIdx]));
        return rasterizer->getProjection() * rasterizer->getModelView() * embed<4>(v);
    }
    virtual bool fragment(Vec3f barycentricCoordinates, TGAColor& color) {
        Vec2f texcoords = uv * barycentricCoordinates;
        Vec3f normal = model->getNormal(texcoords.x, texcoords.y);
        float I = std::max(0.f, normal * light_dir);
        color = model->getDiffuseColor(texcoords.x, texcoords.y) * I;
        return false;
    }
};

在这里插入图片描述
输出图片和教程源码的输出有些不一致
在这里插入图片描述
根据第5节课末尾的内容,假设模型资源里有某个点是(x,y,z,1),这个点对应的向量是(A,B,C,0)。
原本
在这里插入图片描述
如果此时模型的顶点数据经过了一些矩阵变换,可能导致与最初法线贴图记录的法线不再垂直。此时则需要将取出来的法线也做一些矩阵变换,使最终着色时的法线仍与对应顶点垂直
在这里插入图片描述
如图,右边的括号对应的是顶点的变换,则左边对应的是法向量所需的变换
在这里插入图片描述
如图M的逆转置矩阵就是法向量所需的变换
当变换矩阵M是均匀缩放/旋转和平移时,则等于对应的逆转置矩阵,但由于变换矩阵通常包含透视投影,所以一般不相等

struct normalTexShader :public IShader {
    mat<2, 3, float> uv;
    Matrix MVPT = (rasterizer->getProjection() * rasterizer->getModelView()).invert_transpose();
    //....
        Vec3f n = proj<3>(MVPT * embed<4>(normal)).normalize();
        float I = std::max(0.f, n * light_dir);
        //...
    }
};

在这里插入图片描述
和官方图片还是有误差,对比了下代码发现我的写法和教程源码有两点不同
1.教程的光源方向是对比模型的相对位置,光源方向也做了MVP变换

shader.uniform_M = Projection * ModelView;
Vec3f l = proj<3>(uniform_M * embed<4>(light_dir)).normalize();

我的光源位置是绝对位置
逻辑上都是对的,只是取决与具体需求
另一点不同的是,教程源码的法线取值,rgb对应的是zyx

Vec3f Model::normal(Vec2f uvf) {
    Vec2i uv(uvf[0]*normalmap_.get_width(), uvf[1]*normalmap_.get_height());
    TGAColor c = normalmap_.get(uv[0], uv[1]);
    Vec3f res;
    for (int i=0; i<3; i++)
        res[2-i] = (float)c[i]/255.f*2.f - 1.f;
    return res;
}

我取的法线值,rgb就是对应xyz

Vec3f Model::getNormal(float x, float y) {
    TGAColor n = normalTex_.get(x * normalTex_.get_width(), y * normalTex_.get_height());
    Vec3f res;
    for (int i = 0; i < 3; i++) {
        res[i] = n[i] / 255.f * 2 - 1.f;
    }
    return res;
}

这里为了检查后续的显示正确与否,我也先改成教程的逻辑处理
镜面反射

 Vec3f r = (n * (n * l * 2.f) - l).normalize();
 float spec = pow(std::max(r.z, 0.0f), model->getSpecularColor(texcoords.x, texcoords.y));
 float diff = std::max(0.f, n * l);
 TGAColor c = model->getDiffuseColor(texcoords.x, texcoords.y);
 color = c;
 for (int i = 0; i < 3; i++) color[i] = std::min<float>(5 + c[i] * (diff + .6 * spec), 255);

这里教程的逻辑其实比较模糊,没完整的解释光强,反射系数那些布林冯模型公式里的参数
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
正常公式里的颜色系数*光强,在教程里是直接自定义了一些值,环境光的是5,漫反射的是1,镜面反射的是0.6.然后公式里的两个cos值就是教程里求出来的diff和spec,镜面反射贴图记录的就是镜面反射公式的指数p
在这里插入图片描述
项目跟随练习代码地址

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

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

相关文章

2024年区块链,物联网与信息技术国际会议(ICBITIT 2024)

2024年区块链&#xff0c;物联网与信息技术国际会议&#xff08;ICBITIT 2024&#xff09; 2024 International Conference on Blockchain, Internet of Things, and Information Technology 会议简介&#xff1a; 2024年区块链&#xff0c;物联网与信息技术国际会议&#xff…

大数据开发面试题【Kafka篇】

83、介绍下Kafka&#xff0c;Kafka的作用?Kafka的组件?适用场景? kafka是一个高吞吐量、可扩展的分布式消息传递系统&#xff0c;在处理实时流式数据&#xff0c;并能够保证持久性和容错性 可用于数据管道、流分析和数据继承和关键任务应用&#xff08;发布/订阅模式&#…

C语言作为计算机行业的基础之一,是否制约了行业本身的发展?

c不是计算机行业的基础啦&#xff0c;你想&#xff0c;c语言出现时已经有一套成熟的计算机体系&#xff0c;有基于内存地址的寻找指令、数据的工作方式&#xff0c;有汇编语言&#xff0c;那搞出c这种高级语言就很正常啊&#xff01;刚好我有一些资料&#xff0c;是我根据网友给…

C++中的结构体——结构体定义和使用

一、结构体基本概念 结构体属于用户自定义的数据类型&#xff0c;允许用户存储不同的数据类型 二、结构体定义和使用 语法&#xff1a; struct 结构体名 { 结构体成员列表 }&#xff1b; 通过结构体创建变量的方式有三种&#xff1a; 1. struct 结构体名 变量名 2.struct…

智慧农田视频监控技术应用:智能监管引领农业新时代

据新闻报道&#xff0c;5月24日合肥市公安局接到群众报警&#xff0c;反映自己辛苦种植的小麦有几十亩地被人偷偷用收割机盗割。公安机关迅速出警并立案侦查&#xff0c;通过查看监控视频得知&#xff0c;用户所在的公司租用了几千亩土地进行农业种植&#xff0c;因公司与村民之…

C语言 数组——数组的定义和初始化

目录 为什么使用数组(Array)? 一维数组的定义 一维数组的初始化 一维数组元素的访问 一维数组元素的赋值 数组的逻辑存储结构 数组的物理存储结构 二维数组的定义和初始化 为什么使用数组(Array)? 一维数组的定义 一维 数组的定义 int a[10]; 定义一个有 10 个 int 型元素的…

破解微信校验难题,Xinstall助你轻松实现Universal Link功能!

在移动互联网时代&#xff0c;App的推广和运营离不开各种技术手段的支持。其中&#xff0c;Universal Link作为连接App和网页的重要桥梁&#xff0c;被广大开发者所青睐。然而&#xff0c;很多开发者在使用Universal Link时遇到了微信校验不通过的问题&#xff0c;这不仅影响了…

堆排序和Topk问题

堆排序 堆排序即利用堆的思想来进行排序&#xff0c; 总共分为两个步骤&#xff1a; 1. 建堆 升序&#xff1a;建大堆&#xff1b; 降序&#xff1a;建小堆 2 .利用堆删除思想来进行排序 利用堆删除思想来进行排序 建堆和堆删除中都用到了向下调整&#xff0c;因此掌握了…

Go 错误日志处理

是不是所有的 if err ! nil 的地方都应该输出错误日志&#xff1f; 打印过多的错误日志会导致日志文件变得冗长和难以阅读。 其次&#xff0c;重复的错误信息会增加冗余。 此外&#xff0c;每一层都打印错误日志&#xff0c;一旦错误信息设计不当&#xff0c;可能会导致上下…

VCRUNTIME140_1.dll丢失是怎么回事?vcruntime140_1.dll无法继续执行代码的处理方法

VCRUNTIME140_1.dll丢失是怎么回事&#xff1f;问出这样的问题的人&#xff0c;一般是遇到vcruntime140_1.dll无法继续执行代码的问题了&#xff0c;找不到VCRUNTIME140_1.dll文件&#xff0c;那么程序就肯定是启动不了的&#xff0c;程序的启动是需要VCRUNTIME140_1.dll文件的…

全局数据 与 singleton 类的选择

1&#xff0c;singleton 相对于全局数据的优势 使用 Singleton 类相对于全局数据具有以下好处&#xff1a; 1.1. 延迟初始化&#xff1a;Singleton 类可以实现延迟初始化&#xff0c;即在需要时才创建实例&#xff0c;而全局数据在程序启动时就会被初始化。这可以节省资源并提…

设计软件有哪些?建模和造型工具篇(3),渲染100邀请码1a12

这次我们接着介绍建模工具。 1、FloorGenerator FloorGenerator是由CG-Source开发的3ds Max插件&#xff0c;用于快速创建各种类型的地板和瓷砖。该插件提供了丰富的地板样式和布局选项&#xff0c;用户可以根据需要轻松创建木质地板、石板地板、砖瓦地板等不同风格的地面。F…

【常用的队列总结】

文章目录 队列的介绍Queue队列的基本概念与操作队列的基本概念 常见的队列介绍非阻塞队列LinkedList:ArrayDeque:PriorityQueue: 阻塞队列ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue DelayQueueSynchronousQueue 队列的介绍 Queue队列的基本概念与操作 在 …

使用html2canvas和jspdf导出pdf包含跨页以及页脚

首先要下载两个文件&#xff0c;一个为html2canvas.min.js&#xff0c;另一个是jspdf.umd.min.js这两个文件分别下载的地址我也附录上&#xff0c;都在官网git&#xff1a; html2canvas.min.js: https://html2canvas.hertzen.com/dist/html2canvas.min.js jspdf.umd.min.js: …

Docker 快速搭建 MongoDB 4.x 集群(一主一从)

目录 1. 生成 mongo-file2. 启动主节点3. 启动从节点4. 配置副本集5. 注意事项 环境&#xff1a;MongoDB 4.0.25&#xff0c;Alma Linux&#xff08;建议使用 Linux&#xff09; 部署的时候是在同一个及其上操作的&#xff0c;实际可以放在不同机器上。 截止到 2024年05月&…

OceanBase数据库诊断调优,与高可用架构——【DBA从入门到实践】第八期

在学习了《DBA从入门到实践》的前几期课程后&#xff0c;大家对OceanBase的安装部署、日常运维、数据迁移以及业务开发等方面应当已经有了全面的认识。若在实际应用中遇到任何疑问或挑战&#xff0c;欢迎您在OceanBase社区问答论坛中交流、讨论。此次&#xff0c;《DBA从入门到…

如何学到数据库从入门到入土(MySQL篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能接…

以太坊现货ETF获批:引发ETH价格暴涨,市场热议达到高潮

2024年5月24日&#xff0c;北京时间&#xff0c;以太坊现货ETF正式获得美国证券交易委员会&#xff08;SEC&#xff09;的批准&#xff0c;成为继比特币之后&#xff0c;美国主权政府承认的又一加密货币基金产品。这一意外的利好消息引发了加密货币市场的狂欢&#xff0c;以太坊…

阳光电源临摹品引发的EMC正向设计思考

画画可以临摹。画电路板临摹的人更多。 抄板&#xff0c;抄的是过去的板子&#xff0c;容易出问题。现在市场竞争激烈&#xff0c;欧美客户对出口产品的标准要求推陈出新&#xff0c;防不胜防。由于市场的竞争&#xff0c;欧洲客户已经意识到EMC电磁兼容的重要性&#xff0c;不…

【PID算法详解】

PID算法 PID算法介绍用途pid数学表达式及其含义P算法D算法I算法 PID总结数学公式转换代码设计实际运用PID代码实现 PID算法介绍 PID控制器是一种广泛应用于工业控制系统的反馈控制器&#xff0c;它通过比例&#xff08;Proportional&#xff09;、积分&#xff08;Integral&am…