软渲染基础教程(第二部分):绘制直线技巧

更新:11-13 名人轶事 我要投稿 纠错 投诉

各位老铁们好,相信很多人对软渲染基础教程(第二部分):绘制直线技巧都不是特别的了解,因此呢,今天就来为大家分享下关于软渲染基础教程(第二部分):绘制直线技巧以及的问题知识,还望可以帮助大家,解决大家的一些困惑,下面一起来看看吧!

最后通过直线绘制API绘制头部模型

好的,现在开始吧

准备工作

现在我们需要做一些准备工作,因为本教程主要参考的是这个英文教程,所以使用的一些基本代码都是从那里复制的,包括图像文件的创建、写入和保存。并读取模型文件。因为我也做了一些小的修改,所以大家可以从我的github下载代码和资源,有问题可以随时和我交流。

1. 试着生成一张tga图片

//构造了一个*TGAImage*对象用于图像生成,大小为100*100像素。

TGAImage 图像(100, 100, TGAImage:RGB);

//然后将坐标为(40,50)的像素的颜色设置为红色。

image.set(40, 50, TGAColor(255, 0, 0, 255));

//第三行将图像上下翻转(不翻转的话方向就会错,其中奥秘不是很清楚)。

image.flip_vertically();

//第四行保存为tga文件。请注意,如果该文件夹不存在,则不会自动创建。

image.write_tga_file("输出/lesson1/point.tga");执行上面的代码会生成如下图(注意中间左边的点):

Tips: 如果tga图片打不开,这里有一个可以打开tga图片的免费软件。

如果您生成了上面的图像,那么您已经有了一个良好的开端!

2. 增加一个基础数据结构

让我们开始尝试画一条直线。在画直线之前,我们需要准备一个基本的数据结构:

结构体float2

{

浮动x=0;

浮点y=0;

浮点数2() {}

float2(浮点xx, 浮点yy) : x(xx), y(yy) { }

};

3. 画一条直线

好了,重点来了。让我们想象一下绘制直线的界面应该是什么样子。直线应该由连续的点组成。我们制作的接口应该能够接受两个顶点作为参数,然后这个函数可以对两个顶点之间的每个点做一些特殊的操作(比如写入图像)。英文教程提供如下界面:

//x0,y0为点0,x1,y1为点1,image为要写入的图像,color为线条颜色

void line(int x0, int y0, int x1, int y1, TGAImage 图像, TGAColor 颜色);我觉得这样的界面对于后续的工作来说不够友好,所以我打算做一个这样的界面:

void Interpolation(float2 p1, float2 p2, functionhandler);这是一个插值接口,可以通过handler回调p1和p2之间的点。

那么我们画直线的地方就变成了

常量TGAColor 红色=TGAColor(255, 0, 0, 255); //线条颜色

TGAImage 图像(100, 100, TGAImage:RGB); //写入图像的对象

//两个顶点是(10, 10) 和(90, 90)

//那么这条直线应该是从(10, 10) 到(90, 90) 的45 度对角线

//点p是线段上的点,如(10,10),(11,11),(12,12).(90,90)

插值({10, 10}, {90, 90}, [](float2 p) {

image.set(p.x, p.y, 红色);

});

image.flip_vertically();

image.write_tga_file("输出/lesson1/redline.tga"); //输出已经准备好了,现在我们只需要实现插值函数即可。

第一次画线尝试

第一课的目标是渲染线条网格。为了实现这个目标,我们首先需要学会如何画线。在看别人的实现之前,我们可以尝试自己实现一下。要在点(x0, y0) 和点(x1, y1) 之间绘制一条线段,代码可能如下所示:

无效插值(float2 p1,float2 p2,函数处理程序)

{

int x0=p1.x;

int y0=p1.y;

int x1=p2.x;

int y1=p2.y;

对于(浮点数t=0.0;t 1.0;t +=0.01)

{

int x=x0*(1.0 - t) + x1*t;

int y=y0*(1.0 - t) + y1*t;

处理程序(float2(x,y));

}

}得到

第二次尝试

第一个尝试的代码的问题在于该变量(0.01)。如果我们将其更改为0.1,我们的线段将如下所示。

问题出在绘制多少像素,而使用静态值来确定绘制多少像素显然是不正确的,所以我们尝试对代码进行如下修改

无效插值(float2 p1,float2 p2,函数处理程序)

{

int x0=p1.x;

int y0=p1.y;

int x1=p2.x;

int y1=p2.y;

for (int x=x0; x=x1; x++)

{

浮点t=(x - x0)/(浮点)(x1 - x0);

int y=y0*(1. - t) + y1*t;

处理程序(float2(x,y));

}

}我们用下面的代码进行测试

TGAImage 图像(100, 100, TGAImage:RGB);

Interpolation0({ 13, 20 }, { 80, 40 }, [](float2 p) { image.set(p.x, p.y, 白色); });

Interpolation0({ 20, 13 }, { 40, 80 }, [](float2 p) { image.set(p.x, p.y, red); });

Interpolation0({ 80, 40 }, { 13, 20 }, [](float2 p) { image.set(p.x, p.y, red); });

image.flip_vertically();

image.write_tga_file("输出/lesson1/temp.tga");得到结果

结果第一条线显示得很好,第二条线中间有不连续,第三条线根本没画出来。请注意,第一行和第三行以不同的颜色绘制同一条线,只是方向不同。我们看到了白线,但没有看到红线,这暴露了我们的代码的另一个问题:绘制的线段不应该依赖于点的顺序。线段(a,b)和线段(b,a)看起来应该是一样的。

第三次尝试

我们可以通过交换p0和p1来确保x0总是小于x1。

第二线段不连续的原因是线段的高度大于线段的宽度。看来我们可以通过旋转线段来保证线段的高度小于其宽度来解决这个问题:

无效插值(float2 p1,float2 p2,函数处理程序)

{

int x0=p1.x;

int y0=p1.y;

int x1=p2.x;

int y1=p2.y;

布尔陡峭=false;

//确保高度小于宽度

如果(std:abs(x0 - x1)std:abs(y0 - y1))

{

std:swap(x0, y0);

std:swap(x1, y1);

陡峭=真实;

}

//确保从左到右绘制

如果(x0 x1)

{

std:swap(x0, x1);

std:swap(y0, y1);

}

for (int x=x0; x=x1; x++) {

浮点t=(x - x0)/(浮点)(x1 - x0);

int y=y0*(1.0 - t) + y1*t;

if (steep) 处理程序(float2(y, x));

否则处理程序(float2(x,y));

}

}得到

看起来不错

使用最优化的算法

画线是图形渲染的基础,对性能要求极高。因此,我们需要寻找一种更高效的算法。参考这个知乎专栏和英文原版教程,我们终于可以写出这样一个插值算法了:

无效插值(float2 p1,float2 p2,函数处理程序)

{

int x0=p1.x;

int y0=p1.y;

int x1=p2.x;

int y1=p2.y;

布尔陡峭=false;

如果(std:abs(x0 - x1)x1) {

std:swap(x0, x1);

std:swap(y0, y1);

}

int dx=x1 - x0;

int dy=y1 - y0;

int derror2=std:abs(dy) * 2;

整数错误2=0;

整数y=y0;

for (int x=x0; x=x1; x++) {

如果(陡峭)

处理程序(float2(y,x));

别的

处理程序(float2(x,y));

错误2 +=错误2;

如果(错误2 dx){

y +=(y1y0 ? 1 : -1);

错误2 -=dx * 2;

}

}

}

画出模型

我使用的是英文教程中提供的模型代码。以下是模型绘制代码:

常量浮点宽度=1000;

常量浮动高度=1000;

常量TGAColor 白色=TGAColor(255, 255, 255, 255);

模型model("resource/african_head/african_head.obj");

TGAImage图像(宽度,高度,TGAImage:RGB);

for (int i=0; iface=model.face(i);

for (int j=0; j3; j++) {

float3 v1=model.vert(face[j]);

float3 v2=model.vert(face[(j + 1) % 3]);

float2 p1((v1.x + 1.0) * 宽度/2.0, (v1.y + 1.0) * 高度/2.0);

float2 p2((v2.x + 1.0) * 宽度/2.0, (v2.y + 1.0) * 高度/2.0);

插值(p1, p2, [](float2 p) {

image.set(p.x, p.y, 白色);

});

}

}

image.flip_vertically();

用户评论

没过试用期的爱~

终于开始了第二部分,期待学到更多!

    有5位网友表示赞同!

服从

感觉学习软渲染第一步应该先了解一下基本的原理吧?

    有8位网友表示赞同!

怅惘

这条直线的绘制方式很关键啊,肯定能派上用场。

    有15位网友表示赞同!

男神大妈

一直想尝试自己画图,这教程刚好能点亮我!

    有10位网友表示赞同!

景忧丶枫涩帘淞幕雨

希望这篇教程讲详细一点,最好附带代码示例!

    有6位网友表示赞同!

ヅ她的身影若隐若现

学习软渲染是为了更好地理解图像生成过程吧?

    有10位网友表示赞同!

一个人的荒凉

我已经知道硬渲染了,现在来学 SOFT,很期待呢!

    有13位网友表示赞同!

安陌醉生

感觉软件渲染和硬件渲染的区别有点大。

    有16位网友表示赞同!

封锁感觉

画直线是基础啊,掌握之后就能尝试更复杂的图形了!

    有7位网友表示赞同!

微信名字

这教程的进度表是什么样的?

    有7位网友表示赞同!

墨染年华

软渲染比硬渲染更加灵活吧?

    有15位网友表示赞同!

烟雨离殇

我现在只知道一两句关于软渲染,这个教程应该能帮到我。

    有9位网友表示赞同!

迷路的男人

学习新的技术,总要耐心一点慢慢来。

    有20位网友表示赞同!

南宫沐风

不知道这篇教程适合什么入门水平的人。

    有5位网友表示赞同!

孤城暮雨

希望教程能够解决我之前困惑的软渲染问题!

    有14位网友表示赞同!

心脏偷懒

软件渲染确实比较复杂,需要好好钻研。

    有8位网友表示赞同!

纯真ブ已不复存在

这应该是学习计算机视觉必不可少的知识吧!

    有5位网友表示赞同!

半梦半醒i

期待作者分享更多关于软渲染的经验和技巧!

    有18位网友表示赞同!

柠栀

感觉软渲染很有意思,我会认真学习这个教程!

    有7位网友表示赞同!

【软渲染基础教程(第二部分):绘制直线技巧】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活

上一篇:《金瓶梅》第三十五章:桂姐重逢母亲,金莲惊吓孩童 下一篇:《瞬息全宇宙》观影心得与感悟