tinyrenderer-Bresenham绘制直线算法

如何画线段

第一种尝试

求x,y起始点的差值,按平均间隔插入固定点数
起始点平均插入100个点:

void line(int x0, int y0, int x1, int y1, TGAImage& image, TGAColor color) {
	for (float t = 0.; t < 1.; t += .01) {
		int x = x0 + (x1 - x0) * t;
		int y = y0 + (y1 - y0) * t;
		image.set(x, y, color);
	}
}
//...
line(2, 52, 90, 80, image, white);
//...

在这里插入图片描述

问题
线段距离过长,插入固定点数过少时,线段无法连续
插入10个点:

for (float t = 0.; t < 1.; t += .1) {

在这里插入图片描述

第二种尝试

根据 x轴的移动像素比例,给y轴线性插值
注意x轴的移动比例要转成float

void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) { 
    for (int x=x0; x<=x1; x++) { 
        float t = (x-x0)/(float)(x1-x0); 
        int y = y0*(1.-t) + y1*t; 
        image.set(x, y, color); 
    } 
}
//...
line(13, 20, 80, 40, image, white);
line(20, 13, 40, 80, image, red);
line(80, 40, 13, 20, image, red);
//...

在这里插入图片描述
问题

  1. 当y轴移动距离大于x轴移动距离时,y轴的插入值会非常离散,因为y轴的插入频率小于x轴
  2. 起点x必须小于终点x

第三种尝试

判断线段的宽高比(斜率),以长的方向递增做为插值频率
并且对比起点,终点在递增方向轴的大小,以小的点做为递增起始点

void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) { 
    bool steep = false; 
    if (std::abs(x0-x1)<std::abs(y0-y1)) { // if the line is steep, we transpose the image 
        std::swap(x0, y0); 
        std::swap(x1, y1); 
        steep = true; 
    } 
    if (x0>x1) { // make it left−to−right 
        std::swap(x0, x1); 
        std::swap(y0, y1); 
    } 
    for (int x=x0; x<=x1; x++) { 
        float t = (x-x0)/(float)(x1-x0); 
        int y = y0*(1.-t) + y1*t; 
        if (steep) { 
            image.set(y, x, color); // if transposed, de−transpose 
        } else { 
            image.set(x, y, color); 
        } 
    } 
}
//...
line(13, 20, 80, 40, image, white);
line(20, 13, 40, 80, image, red);
line(80, 40, 13, 20, image, red);
//...

在这里插入图片描述
问题
效率低,做了多次除法和浮点数运算。10%的时间花在复制颜色上。但是70%的时间都花在了调用line()上
没有断言,没有越界检查等(这些文章里为了可读性,所以不处理)

第四种尝试

每个除法都有相同的除数,可以提到循环外
在这里插入图片描述
我们是以长轴每次递增1做为循环,因此另一个轴每次递增的插值是在[0,1]之间。根据斜率决定。
这里我们假设长轴是x,如上图,每次x递增1时,y轴根据斜率判断增加的值,如果大于0.5时,则代表了线段的y值是在右上的格子(y+1),小于0.5表示线段的y值是在右边的格子(y不变)
由此可以根据斜率判断每次x递增时,y轴的值是否需要+1
循环内省去了除法计算,减少了浮点数计算
float t = (x - x0) / (float)(x1 - x0);
int y = y0 * (1. - t) + y1 * t;

void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) { 
    bool steep = false; 
    if (std::abs(x0-x1)<std::abs(y0-y1)) { 
        std::swap(x0, y0); 
        std::swap(x1, y1); 
        steep = true; 
    } 
    if (x0>x1) { 
        std::swap(x0, x1); 
        std::swap(y0, y1); 
    } 
    int dx = x1-x0; 
    int dy = y1-y0; 
    float derror = std::abs(dy/float(dx)); 
    float error = 0; 
    int y = y0; 
    for (int x=x0; x<=x1; x++) { 
        if (steep) { 
            image.set(y, x, color); 
        } else { 
            image.set(x, y, color); 
        } 
        error += derror; 
        if (error>.5) { 
            y += (y1>y0?1:-1); 
            error -= 1.; 
        } 
    } 
} 

问题
仍然还有一个浮点数计算及比较
error += derror;

第五次尝试

error浮点数在循环体中的作用就是来用与0.5做大小比较
error += derror;
if (error > .5)
error -= 1.;

==>> error每次变化量都乘2
error += derror * 2;
if (error > 1)
error -= 2;

==>> error每次变化量都乘dx
error += derror * 2 * dx;
if (error > dx)
error -= 2 * dx;

==>> derror * 2 * dx 是int型 std::abs(dy)*2;
==>>error也不需要再是float

void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) { 
    bool steep = false; 
    if (std::abs(x0-x1)<std::abs(y0-y1)) { 
        std::swap(x0, y0); 
        std::swap(x1, y1); 
        steep = true; 
    } 
    if (x0>x1) { 
        std::swap(x0, x1); 
        std::swap(y0, y1); 
    } 
    int dx = x1-x0; 
    int dy = y1-y0; 
    int derror2 = std::abs(dy)*2; 
    int error2 = 0; 
    int y = y0; 
    for (int x=x0; x<=x1; x++) { 
        if (steep) { 
            image.set(y, x, color); 
        } else { 
            image.set(x, y, color); 
        } 
        error2 += derror2; 
        if (error2 > dx) { 
            y += (y1>y0?1:-1); 
            error2 -= dx*2; 
        } 
    } 
} 

消除循环中的分支
将steep判断移到for循环外,可以通过牺牲一些代码膨胀,把速度提高到2倍
(现代一些编译器会自动移出到for循环外)

if(steep) {
        for(int x = x0; x<=x1; ++x) {
            img.set_pixel_color(y, x, color);
            error2 += derror2;
            if(error2 > dx) {
                y += (y1>y0? 1 : -1);
                error2 -= dx*2;
            }
        }
    } else {
        for(int x = x0; x<=x1; ++x) {
            img.set_pixel_color(x, y, color);
            error2 += derror2;
            if(error2 > dx) {
                y += (y1>y0? 1 : -1);
                error2 -= dx*2;
            }
        }
    }

线框显示

导入了一个模型数据读取的类model.h,model.cpp和向量几何运算的类geometry.h

obj文件的数据:
v 0.608654 -0.568839 -0.416318
其中“v "开头的数据代表了模型顶点的位置,归一化之后的。取值在[-1,1],实际根据自身屏幕的宽高进行转化
f 1193/1240/1193 1180/1227/1180 1179/1226/1179
"f "储存了每个面(三角形)的信息,每组数字的第一个代表了前面对”v “开头的顶点数据的索引。obj文件的索引是从1开始,因此从c++的数组读取时,索引要-1读取

for (int i=0; i<model->nfaces(); i++) { 
    std::vector<int> face = model->face(i); 
    for (int j=0; j<3; j++) { 
        Vec3f v0 = model->vert(face[j]); 
        Vec3f v1 = model->vert(face[(j+1)%3]); 
        int x0 = (v0.x+1.)*width/2.; 
        int y0 = (v0.y+1.)*height/2.; 
        int x1 = (v1.x+1.)*width/2.; 
        int y1 = (v1.y+1.)*height/2.; 
        line(x0, y0, x1, y1, image, white); 
    } 
}

在这里插入图片描述
项目跟随练习代码地址

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

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

相关文章

力扣每日一题 2024/3/19 好子数组的最大分数

题目描述 用例说明 思路讲解 好子数组的下标在i<k<j,即nums[k]必须在好子数组当中&#xff0c;将数组以k为分界点分为左右两半&#xff0c;左半left从k-1向左移动&#xff0c;右半right从k1开始出发向右移动。 当left大于等于分界点的值时左移一位&#xff0c;当right大…

tp8 mpdf 导出pdf

1. 安装mpdf composer require mpdf/mpdf 2. 然后 使用 use mpdf\Mpdf; 或者 require_once __DIR__ . /vendor/autoload.php; 官方文档 mPDF – mPDF 手册 文档里有很多东西 可以自己去研究 3. 编写代码 下载 (支持中文) $mpdf new Mpdf([mode > utf-8,"autoS…

CMeet系列技术生态沙龙---《探索未来:生成式AI赋能千行百业·杭州》

当前数字化浪潮下&#xff0c;生成式AI技术正成为推动产业升级、提升竞争力的关键力量。为深入探索未来AI技术的赋能作用&#xff0c;促进技术生态的繁荣与发展&#xff0c;CSDN-CMeet系列沙龙活动旨在搭建一个交流与探索的平台&#xff0c;通过分享前沿研究成果和应用案例&…

服务器数据恢复—光纤环境互斥不当导致存储VMFS卷损坏的数据恢复案例

服务器数据恢复环境&故障&#xff1a; 某公司的信息管理平台&#xff0c;通过3台虚拟机共享了一台存储设备供企业内部使用&#xff0c;存储设备中存放了公司内部重要的数据文件。 由于业务增长的需要&#xff0c;管理员又在这个存储网络上连接了一台Windows server服务器&a…

媒体邀约:推广方法

1.媒体邀约推广媒体邀约推广是一种通过与商务合作以增强曝光度和名气的营销手段。积极与商务合作&#xff0c;公司能将产品和服务推广给更大范围受众人群&#xff0c;以达到增加销量和市场占有率目地。 1.1 选择适合自己的新闻媒体选择适合自己的新闻媒体是媒体邀约推广的关键…

sqllab第35-45关通关笔记

35关知识点&#xff1a; 宽字节注入数值型注入错误注入 payload:id1andextractvalue(1,concat(0x7e,database(),0x7e))0--联合注入 payload:id0unionselect1,database(),version()-- 36关知识点&#xff1a; 字符型注入宽字节注入错误注入 payload:id1%df%27andextractvalue(…

Qt实现简单的五子棋程序

Qt五子棋小程序 Qt五子棋演示及源码链接登陆界面单机模式联机模式联网模式参考 Qt五子棋 参考大佬中国象棋程序&#xff0c;使用Qt实现了一个简单的五子棋小程序&#xff0c;包含了单机、联机以及联网三种模式&#xff1b;单机模式下实现了简易的AI&#xff1b;联机模式为PtoP…

由于找不到kvpvbsext64.dll,无法继续执行代码。解决办法,

kvpvbsext64.dll 是一个动态链接库文件&#xff0c;通常作为某个软件的一部分存在。具体来说&#xff0c;它可能为某个程序的特定功能提供支持&#xff0c;在软件运行时被调用和使用。因此&#xff0c;当出现与该文件相关的错误时&#xff0c;可能会影响到相应软件的正常运行。…

ModbusTCP转Profinet网关高低字节交换切换

背景&#xff1a;在现场设备与设备通迅之间通常涉及到从一种字节序&#xff08;大端或小端&#xff09;转换到另一种字节序。大端字节序是指高位字节存储在高地址处&#xff0c;而小端字节序是指低位字节存储在低地址处。在不动原有程序而又不想或不能添加程序下可选用ModbusTC…

算法沉淀——贪心算法二(leetcode真题剖析)

算法沉淀——贪心算法二 01.最长递增子序列02.递增的三元子序列03.最长连续递增序列04.买卖股票的最佳时机 01.最长递增子序列 题目链接&#xff1a;https://leetcode.cn/problems/longest-increasing-subsequence/ 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子…

Matplotlib数据可视化实战-1数据可视化Matplotlib基础

1.1绘图的一般过程&#xff1a; 1.导入相关库 2.生成、读入或计算得到数据&#xff1b; 3.根据需要绘制折线图、散点图、柱状图、饼状图、雷达图、箱线图、三维曲线/曲面以及极坐标系图形&#xff1b; 4.根据需要设置图形属性&#xff1b; 5.显示或保存绘图结果。 例如&…

缺失的第一个正数-面试热题 100?-Lua 中文代码解题第5题

缺失的第一个正数-面试热题 100&#xff1f;-Lua 中文代码解题第5题 解题思路&#xff1a; 遍历数组并尝试将元素放入正确的位置&#xff1a; 遍历输入数组 nums&#xff0c;对于每个元素 nums[i]&#xff1a; 如果 nums[i] 是一个正整数&#xff0c;并且它的值小于或等于数组…

Windows Server 2012 R2在安装软件的时候显示乱码

1、打开控制面板——时钟、语言和区域——语言&#xff0c;添加为汉语 2、接着选择区域为中国 3、完美解决

Covalent Network(CQT)借助最大规模的历史与实时 Web3 数据集,推动人工智能的发展

人工智能在众多领域中增强了区块链的实用性&#xff0c;反之亦然&#xff0c;区块链确保了 AI 模型所使用的数据的来源和质量。人工智能带来的生产力提升&#xff0c;将与区块链系统固有的安全性和透明度融合。 Covalent Network&#xff08;CQT&#xff09;正位于这两项互补技…

day11【网络编程】

day11【网络编程】 主要内容 软件架构CS&#xff0f;BS网络通信三要素TCP通信Socket套接字ServerSocket 目标 能够辨别UDP和TCP协议特点 能够说出TCP协议下两个常用类名称 能够编写TCP协议下字符串数据传输程序 能够理解TCP协议下文件上传案例 能够理解TCP协议下案例2 第一…

dockers拉取MySQL及Redis并挂载文件

目录 一 . MySQL拉取 1、进入 MySQL 容器内部。 2、登录 MySQL。 3、修改远程连接 4、刷新 二 . Redis拉取 1 . redis/conf中新建文件redis.conf&#xff0c;内容如下&#xff1a; 2 . 容器运行 一 . MySQL拉取 docker run -d --restartalways --name mysql \ -v /…

基于Spring Boot的中医学习服务管理系统

摘 要 随着世界经济信息化、全球化的到来和互联网的飞速发展&#xff0c;推动了各行业的改革。若想达到安全&#xff0c;快捷的目的&#xff0c;就需要拥有信息化的组织和管理模式&#xff0c;建立一套合理、动态的、交互友好的、高效的中医学习服务管理系统。当前的信息管理存…

【拓扑排序】有向图的拓扑排序

问题描述 给定一个 n 个点 m 条边的有向图&#xff0c;点的编号是 1 到 n&#xff0c;图中可能存在重边和自环。 请输出任意一个该有向图的拓扑序列&#xff0c;如果拓扑序列不存在&#xff0c;则输出 −1。 若一个由图中所有点构成的序列 A 满足&#xff1a;对于图中的每条…

超长期特别国债来了!你想了解的都在这里!

今年全国两会&#xff0c;超长期特别国债成为广受关注的热点话题之一。政府工作报告在“积极的财政政策要适度加力、提质增效”总体要求下&#xff0c;明确重要增量举措&#xff1a;从今年开始拟连续几年发行超长期特别国债&#xff0c;专项用于国家重大战略实施和重点领域安全…

深入解析Go语言`errors`库:提升你的错误处理技能

深入解析Go语言errors库&#xff1a;提升你的错误处理技能 引言Go的错误处理简介错误作为值error接口简单错误处理示例 errors包概览创建错误错误格式化错误检查示例&#xff1a;错误检查 创建和使用自定义错误定义自定义错误类型使用自定义错误错误包装错误处理的灵活性 错误检…