tinyrenderer-Bresenham绘制直线算法

news2024/11/15 12:17:47

如何画线段

第一种尝试

求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); 
    } 
}

我这里给model加了个maxNum,用来适配obj顶点数据是非标准化设备坐标
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

项目跟随练习代码地址

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

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

相关文章

2024计算机二级Python 11和12

单向列表不能再回头&#xff0c;只有从头指针开始才可以&#xff0c;双向列表会出现重复访问&#xff0c;二叉树节点从根开始可以达到目的 面向对象的主要特征&#xff1a;抽象、封装、继承、多态 Python通过解释方式执行&#xff0c;执行速度没有采用编译方式的语言执行的快 f…

Docker【docker使用】

文章目录 前言一、概念二、常用方法1.镜像2.容器 三、镜像与镜像的关系&#xff0c;以及镜像构建和管理 前言 上一篇文章讲了docker的安装&#xff0c;本片文章我们来聊聊docker的一些常用操作。以及镜像、容器之间的关系 如果你想更加系统的学习docker&#xff0c;请看【dock…

蓝桥杯(3.21 刷真题)

P8682 [蓝桥杯 2019 省 B] 等差数列 import java.util.Arrays; import java.util.Scanner; public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int[] res new int[n1];for(int i1;i<n;i)res[i] sc.ne…

elasticsearch的数据搜索

DSL查询文档 elasticsearch的查询依然是基于JSON风格的DSL来实现的。 Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括: 查询所有:查询出所有数据,一般测试用。例如:match_all 全文检索(full text)查询:利用分词器对用户…

虚拟直播赋能文旅,蓝海创意云亮相文旅虚拟现实应用推广交流活动

3月21日&#xff0c;由文化和旅游部产业发展司主办&#xff0c;中国信息通信研究院、北京市石景山区文化和旅游局、中国动漫集团有限公司承办的文化和旅游虚拟现实应用推广交流活动在首钢一高炉SoReal科幻乐园33 Meta Club举办。蓝海创意云应邀参与此次活动&#xff0c;携vLive…

ON1 NoNoise AI 2024 for mac v18.2 中文版 ai摄影

ON1 NoNoise AI 2024 for mac一款去除图像噪点的应用&#xff0c;特别对于摄影师来说&#xff0c;它是比较专业的摄影降噪软件。 软件下载&#xff1a;ON1 NoNoise AI 2024 for mac v18.2 中文版下载 智能降噪&#xff1a;利用先进的机器学习技术&#xff0c;ON1 NoNoise AI 20…

第5讲-MIPS(3)汇编语言

三、MIPs汇编语言(自学) 1.概述

taro之Picker,PickerView基础用法

1.Picker 直接上代码 import Taro,{Component} from "tarojs/taro"; import {View,Picker} from tarojs/components import { AtIcon } from taro-ui import { putKey } from /src/utils/storage-utilsclass AgriculturePolicy extends Component{constructor (prop…

部署Zabbix Agents添加使能监测服务器_Linux平台_Yum源/Archive多模式

Linux平台 一、从yum源脚本安装部署Zabbix-Agent,添加Linux Servers/PC 概述 Zabbix 主要有以下几个组件组成: Zabbix Server:Zabbix 服务端,Zabbix的核心组件,它负责接收监控数据并触发告警,还负责将监控数据持久化到数据库中。 Zabbix Agent:Zabbix客户端,部署在被监…

【综述】二维半导体和晶体管在集成电路未来应用

一篇关于二维半导体和晶体管在集成电路未来应用的综述文章。 文章由Lei Yin、Ruiqing Cheng、Jiahui Ding、Jian Jiang、Yutang Hou、Xiaoqiang Feng、Yao Wen和Jun He*共同撰写&#xff0c;发表在《ACS Nano》2024年第18卷上。 Figure 1: CMOS晶体管的演变 描述了CMOS晶体管…

8 克隆虚拟机

后期集群我们需要使用多台服务器&#xff0c;此处我们先克隆三台&#xff0c;master,slave01,slave02. 1.先关闭模版虚拟机。再选择 模版虚拟机hadoop100右击--》管理 --》克隆 2.下图中特别注意&#xff1a;建议使用集群的名字作为虚拟机名称。目前克隆主机master. 以上步骤完…

全域电商数据实现高效稳定大批量采集♀

全域电商&#xff0c;是近几年的新趋势&#xff0c;几乎所有商家都在布局全域&#xff0c;追求全域增长。但商家发现&#xff0c;随着投入成本的上涨&#xff0c;利润却没有增加。 其中最为突出的是——商家为保证全域数据的及时更新&#xff0c;通过堆人头的方式完成每日取数任…

怎么自己打印物流货运单,物流打印定制软件

怎么自己打印物流货运单&#xff0c;物流打印定制软件 一、前言 以下软件操作教程以 佳易王物流单打印登记查询系统V17.0为例说明 件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、物流货运单打印有两种模式 一个是直接打印&#xff0c;可以在空白纸上…

Matlab与高光谱遥感:环境监测的新时代

光谱和图像是人们观察世界的两种方式&#xff0c;高光谱遥感通过“图谱合一”的技术创新将两者结合起来&#xff0c;大大提高了人们对客观世界的认知能力&#xff0c;本来在宽波段遥感中不可探测的物质&#xff0c;在高光谱遥感中能被探测。以高光谱遥感为核心&#xff0c;构建…

docker 配置国内阿里镜像源

在/etc/docker/目录下新建daemon.json文件 在文件中写入 {"registry-mirrors": ["https://jmphwhtw.mirror.aliyuncs.com"] } 以管理员身份运行命令 systemctl daemon-reload systemctl restart docker

JetPack之DataBinding基础使用

目录 一、简介二、使用2.1 使用环境2.2 xml文件绑定数据2.3 数据绑定的对象2.3.1 object2.3.2 ObseravbleField2.3.3 ObseravbleCollection 2.4 绑定数据 三、应用场景 一、简介 DataBinding是谷歌15年推出的library,DataBinding支持双向绑定&#xff0c;能大大减少绑定app逻辑…

【Algorithms 4】算法(第4版)学习笔记 20 - 5.1 字符串排序

文章目录 前言参考目录学习笔记1&#xff1a;Java 字符串1.1&#xff1a;字符串数字类型2&#xff1a;键索引计数法 key-indexed counting2.1&#xff1a;排序算法回顾2.2&#xff1a;关于 key 的假设2.3&#xff1a;demo 演示2.4&#xff1a;分析3&#xff1a;低位优先基数排序…

全球首个Open机器人诞生!当GPT模型有了“肉体”,不仅能听能写还可以干家务!

世界上第一个「ChatGPT机器人」来了&#xff01;近期&#xff0c;初创公司美国机器人创业公司Figure发布首个基于OpenAI多模态大模型的人型机器人Figure 01。现在直接给LLM造了个身体&#xff0c;具体来说是个OpenAI训练的多模态大模型。 Figure AI发布了一段引人注目的视频&…

计算机硕士,毕业直接后端开发岗,选择C++还是java?

我自己是一名工作多年的C程序员&#xff0c;大学学习的编程语言就是C/C&#xff0c;参加工作后自学了Python、Java、Golang等语言。 现在从事自动驾驶行业的工作&#xff0c;工作中主要使用的编程语言是C和Python。在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整…

外包干了15天,技术退步明显。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;2019年我通过校招踏入了南京一家软件公司&#xff0c;开始了我的职业生涯。那时的我&#xff0c;满怀热血和憧憬&#xff0c;期待着在这个行业中闯出一片天地。然而&#xff0c;随着时间的推移&#xff0c;我发现自己逐渐陷入…