TinyRenderer学习笔记--Lesson 3、4

news/2024/5/18 21:44:51/文章来源:https://blog.csdn.net/qq_51599283/article/details/126604670

Lesson 3 zbuffer

无论怎样,生活中的显示器基本上都是平面,是一个2D的场景,而我们的模型却是3D的,是有深度的,实际上我们看见的都只是离我们的眼睛最近的那一个平面,一个不透明的3D物体的内部和背面是我们无法观测到的。对应到计算机里面,我们就需要知道一个物体哪个平面离我们的虚拟摄像机最近,但是这往往是很难办到的,如下:

image-20220814145444624

显然,我们无法判断绿色三角形和粉色三角形哪个离平面更近,这时就不能以三角形平面为单位来绘制,需要以像素为单位来绘制。

void triangle(Vec3f* pts, float* zbuffer, TGAImage& image, TGAColor color)
{//定义包围盒Vec2f bboxmin(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());Vec2f bboxmax(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max());Vec2f clamp(image.get_width() - 1, image.get_height() - 1);//找到包围盒for (int i = 0; i < 3; ++i){for (int j = 0; j < 2; ++j){bboxmin[j] = std::max(0.f, std::min(bboxmin[j], pts[i][j]));bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j],pts[i][j]));}}Vec3f p;for (p.x = bboxmin.x; p.x < bboxmax.x; p.x++){for (p.y = bboxmin.y; p.y < bboxmax.y; p.y++){//找到重心坐标并判断判断是否在三角形内Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], p);if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0) continue;p.z = 0;//通过重心坐标计算深度值for (int i = 0; i < 3; i++) p.z += pts[i][2] * bc_screen[i];if (zbuffer[int(p.x + p.y * width)] < p.z){//更新深度值zbuffer[int(p.x + p.y * width)] = p.z;image.set(p.x, p.y, color);}}}}.........Vec3f light_dir(0, 0, -1);//深度缓冲区,并赋值float* zbuffer = new float[width * height];for (int i = width * height; i--; zbuffer[i] = -std::numeric_limits<float>::max());for (int i = 0; i < model->nfaces(); i++) {std::vector<int> face = model->face(i);Vec3f pts[3];Vec3f world_coords[3];for (int j = 0; j < 3; ++j){Vec3f v = model->vert(face[j]);pts[j] = world2screen(model->vert(face[j]));world_coords[j] = v;}Vec3f n = cross((world_coords[2] - world_coords[0]),(world_coords[1] - world_coords[0]));n.normalize();float intensity = n * light_dir;//光照强度=法向量*光照方向   即法向量和光照方向重合时,亮度最高//强度小于0,说明平面朝向为内  即背面裁剪if (intensity > 0) {triangle(pts,zbuffer, image, TGAColor(intensity * 255, intensity * 255, intensity * 255, 255));}} 

在我们之前上一节实现的平面着色上增加深度值的检测,会让我们的图象看起来更加立体

image-20220814150141677

下面将要给我们的模型加上贴图,让渲染的模型看起来更加真实。这里我们采用重心坐标插值的办法来进行纹理贴

图,首先我们需要知道某个三角形顶点上的UV值,然后通过插值的办法计算出三角形内部某个点的UV值,OBJ文

件里已经保存了顶点的纹理坐标和纹理信息,只需要进行一次插值计算即可。关键代码如下:

Vec3f p;
for (p.x = bboxmin.x; p.x < bboxmax.x; p.x++)
{for (p.y = bboxmin.y; p.y < bboxmax.y; p.y++){//找到重心坐标并判断是否在三角形内Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], p);if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0) continue;p.z = 0;//重心坐标插值计算UV值Vec2i uvp = uv[0] * bc_screen.x + uv[1] * bc_screen.y + uv[2] * bc_screen.z;//通过重心坐标计算深度值for (int i = 0; i < 3; i++) p.z += pts[i][2] * bc_screen[i];if (zbuffer[int(p.x + p.y * width)] < p.z){//更新深度值zbuffer[int(p.x + p.y * width)] = p.z;TGAColor color = model->diffuse(uvp); //找到对应纹理image.set(p.x, p.y, TGAColor(color.r * intensity, color.g * intensity, color.b * intensity, 255));}}
}
    if (intensity > 0) {Vec2i uv[3];for (int k = 0; k < 3; k++) {uv[k] = model->uv(i, k);//获取三个顶点的UV值}triangle(pts,zbuffer,uv, image,intensity);}

注意,这里要对model.h和geometry.h及.cpp文件进行修改,详情参考

效果如下:

image-20220830094648272

Lesson 4 透视投影

投影大致可以分为透视投影和正交投影

image-20220819092155185

透视投影的最直观的效果就是近大远小。

对于透视投影的计算,我们需要进行简单的探讨,这里就不在讨论,强烈建议大家查看文章

文章中对缩放矩阵,平移矩阵等进行了详细的探讨。

image-20220819094033667

其中 r = -1/c,

接下来就是进行编码了。代码大体上和上一节差不多,多的部分就是利用矩阵来实现透视投影。

两个功能函数,4D变3D和3D变4D,和一个视角转换函数,


Vec3f m2v(Matrix m)
{return Vec3f(m[0][0] / m[3][0], m[1][0] / m[3][0], m[2][0] / m[3][0]);
}Matrix v2m(Vec3f v)
{Matrix m(4,1);m[0][0] = v.x;m[1][0] = v.y;m[2][0] = v.z;m[3][0] = 1.f;//添加一个1表示坐标return m;
}
//视图矩阵,把模型坐标的(-1,1)转换成屏幕坐标的(100,700)
//zbuffer从(-1,1)转换成0~255
Matrix viewport(int x, int y, int w, int h) {Matrix m = Matrix::identity(4);//平移m[0][3] = x + w / 2.f;m[1][3] = y + h / 2.f;m[2][3] = depth / 2.f;//缩放m[0][0] = w / 2.f;m[1][1] = h / 2.f;m[2][2] = depth / 2.f;return m;
}

对投影矩阵和视角矩阵进行初始化,这里注意投影矩阵的[3] [2]坐标

    //初始化透视投影矩阵Matrix Projection = Matrix::identity(4);//初始化视角矩阵Matrix ViewPort = viewport(width / 8, height / 8, width * 3 / 4, height * 3 / 4);//投影矩阵[3][2]=-1/c,c为相机z坐标Projection[3][2] = -1.f / camera.z;

有了这三个矩阵,在计算屏幕坐标的时候,直接进行乘就行,简称为MVP变换,M是模型矩阵,V是视角矩阵,P是投影矩阵。

        for (int j = 0; j < 3; j++) {Vec3f v = model->vert(face[j]);//视角矩阵*投影矩阵*坐标screen_coords[j] = m2v(ViewPort * Projection * v2m(v));world_coords[j] = v;}

就在这里应用,和原来的代码的主要不同之处也就在这。最后结果如下

image-20220830150425580

下面是深度图:

image-20220830150504984

现在通过学习,已经学会了

三角形的栅格化及背面剔除 (通过实现不同光照来实现背部剔除)

zbuffer深度缓冲区

纹理贴图 (利用重心坐标插值)

透视投影 (MVP变换)

之处也就在这。最后结果如下

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

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

相关文章

河北稳控科技使用标准信号检测 VM振弦采集模块测量精度

河北稳控科技使用标准信号检测 VM振弦采集模块测量精度(一) (1)电源1.1VDD 引脚电源必须使用 LDO 稳压或者低纹波线性电源, LDO 推荐使用 AM1117_3.3V 芯片,测试时发现 SPX 生产的 LDO会造成非常严重的干扰(其它品牌应该也会有类似的问题)。1.2VSEN 引脚电源单通道模块…

阿里、滴滴、华为等一线互联网分布式消息中间件:RocketMQ核心笔记

本篇介绍了RocketMQ的基本使用方法及其各个组件的基本原理&#xff0c;讲解原理时&#xff0c;都是采用先整体架构后详细分解的方式。详细分解时不会深入源码逐段讲&#xff0c;而是从代码结构出发梳理整个运行过程。 这份RocketMQ分布式消息中间件—核心原理与最佳实践的完整…

Android Studio应用基础,手把手教你从入门到精通(小白学习)总结2 之 常用界面布局和ListView

总结1链接&#xff1a; (156条消息) Android Studio应用基础&#xff0c;手把手教你从入门到精通&#xff08;小白学习&#xff09;总结1_好喜欢吃红柚子的博客-CSDN博客 学习视频链接&#xff1a; &#xff08;学完必会&#xff09;Android studio基础&#xff0c;从入门到…

尚好房 07_前端房源展示

尚好房&#xff1a;前端房源展示 一、分页显示房源列表 1、效果 2、项目搭建 2.1 创建项目 在web项目中创建子工程web-front 2.2 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0&…

stm32学习(二)|ADC电压采集DMA

利用ADC通道采集外部传感器数值,ADC通道选择依据实际查询芯片手册可得,相关配置利用Cubemx完成。 ADC参数配置首先选择需要使用的ADC通道,并设置对应的引脚ADC_IN0X.ADC参数设置(Paremeter setting)Mode : Independent mode,只使用一个ADC通道 Clock Prescaler,Resolut…

OpenGL 反色

目录 一.OpenGL 反色 1.IOS Object-C 版本2.Windows OpenGL ES 版本3.Windows OpenGL 版本 二.OpenGL 反色 GLSL Shader三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >&…

Windows OpenGL ES 图像反色

目录 一.OpenGL ES 图像反色 1.原始图片2.效果演示 二.OpenGL ES 图像反色源码下载三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基础 OpenGL E…

责任链模式

1、责任链模式是什么 行为模式&#xff0c;一个对象产生的消息会被另外的对象处理。对象发出消息后&#xff0c;不管被哪种、多少个其他对象收到和处理消息。【客户端和handler解耦】 2、为什么使用 如果不使用责任链&#xff0c;则client要知道有多少个handler、什么情况调…

2.IP子网划分

IP子网划分地址分类网络位与主机位一个网段可以容纳多少IPIP地址&#xff1a;互联网中计算机的‘身份证号’&#xff0c;唯一标识一台网络设备的身份ID NAT技术&#xff1a;网络地址转换&#xff0c;节约公网IP 例: IP地址 192.168.1.1 192.168.1 …

电商数仓项目中各层的表

ODS operation Data store 操作数据存储 DWD Data Warehouse detail 细节数据层, DIM Dimension---------------范围&#xff0c;维度 DWS Data Warehouse Summary 数据库汇总 ADS Application Data Service 应用数据服务层 【电商数仓每一层的表】 【ODS层】 operation Data s…

Spring之AOP思想

目录 什么是AOP ​​​为什么用AOP Spring AOP 应该怎么学习呢 AOP下的一些核心概念&#xff08;SpringAOP并没有实现所有的概念&#xff09; 基于概念的使用Spring的AOP 一个使用的实例 关于切点的匹配 通知的种类 使用注解的方式来实现功能​编辑 AOP框架背后的核心 …

TypeScript 小结

TypeScript 是什么&#xff1f; TypeScript 是由微软开发的一种自由和开源的编程语言。它是 JavaScript 的一个超集&#xff0c;本质上是在 JavaScript 的基础上添加了可选的静态类型和基于类的面向对象编程。 TypeScript 和 JavaScript 的区别&#xff1f; TypeScript 的安装…

Netty(10)协议设计与解析(IdleStateHandler:空闲检测器、心跳)

为什么需要协议&#xff1f; TCP/IP 中消息传输基于流的方式&#xff0c;没有边界。 协议的目的就是划定消息的边界&#xff0c;制定通信双方要共同遵守的通信规则 协议举例 redis 协议 客户端代码 import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf…

Webmin -- Sheduled Commands

at作业(Webmin称之为预定命令)类似Scheduled Cron Jobs&#xff0c;但不是按调度重复地执行&#xff0c;而是仅在指定的日期和时间运行一次。不同于Cron作业&#xff0c;可以配置它们在指定目录而不是在用户的家目录中执行。预定的命令也跟踪在创建时设置的环境变量&#xff0c…

C++ 哈希桶模拟实现(补充)

目录 定义基本的存储结构 Insert()和Find() Erase() 如何控制哈希冲突&#xff1f; Insert()中添加扩容操作 其他问题的解决 UnorderedMap.h和UnorderedSet.h 迭代器实现与UnorderedMap.h和UnorderedSet.h的封装 定义基本的存储结构 #pragma once #include<iostream&…

Rethinking the Inception Architecture for Computer Vision--Christian Szegedy

Christian Szegedy, Vincent Vanhoucke, Sergey Ioffe, Jonathon Shlens, & Zbigniew Wojna (2016). Rethinking the Inception Architecture for Computer Vision computer vision and pattern recognition. 0、摘要1、引入2、通用设计原则2.1 避免表征瓶颈2.2 特征数据越…

安卓毕业设计成品基于Uniapp+SSM实现的智能课堂管理APP在线学习网

&#x1f496;&#x1f496;更多项目资源&#xff0c;最下方联系我们✨✨✨✨✨✨ 目录 Uniapp项目介绍 资料获取 Uniapp项目介绍 计算机毕业设计安卓App毕设项目之ssm智能课堂管理APP-IT实战课堂_哔哩哔哩_bilibili计算机毕业设计安卓App毕设项目之ssm智能课堂管理APP-IT实…

常用的基本命令(必掌握)

目录 常用的基本命令&#xff08;必掌握&#xff09; 目录管理 基本属性 修改文件属性 文件内容查看 拓展&#xff1a;Linux 链接概念 常用的基本命令&#xff08;必掌握&#xff09; 目录管理 绝对路径和相对路径 我们知道Linux的目录结构为树状结构&#xff0c;最顶级…

有序的Map集合

我们通常使用的Map集合是HashMap&#xff0c;在大多数情况下HashMap可以满足我们的要求&#xff0c;但是HashMap有一个缺点&#xff1a;HashMap****是无序的&#xff0c;即其迭代顺序与其key或value的大小无关。而在某些情况下&#xff0c;如果我们需要Map集合里的元素有序&…

HTML5期末大作业:商城网站设计——仿天猫在线商城(HTML和CSS实现天猫在线商城网站)

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…