Note: This series blog was translated from Nathan Vaughn's Shaders Language Tutorial and has been authorized by the author. If reprinted or reposted, please be sure to mark the original link and description in the key position of the article after obtaining the author’s consent as well as the translator's. If the article is helpful to you, click this Donation link to buy the author a cup of coffee.

说明:该系列博文翻译自Nathan Vaughn着色器语言教程。文章已经获得作者翻译授权,如有转载请务必在取得作者译者同意之后在文章的重点位置标明原文链接以及说明。如果你觉得文章对你有帮助,点击此打赏链接请作者喝一杯咖啡。

朋友们,你们好!在上一篇文章中,我们学习了如何绘制一个圆形并且让它动起来。在这篇文章中,我们将会学习如何绘制一个方块并且用一个旋转矩阵让它转起来。

绘制矩形

绘制一个矩形和绘制一个圆形是非常相似的,我们只需要用另外一个等式。实际上,只需要我们写出一个正确的等式,我们就能几乎能绘制出它对应的图形。矩形的等式如下:

  max(abs(x),abs(y)) = r

  x = x-coordinate on graph
y = y-coordinate on graph
r = radius of square

我们调整等式和变量的顺序,使得等式为0:

  max(abs(x), abs(y)) - r = 0

我们把它改造成下面的样子,就可以Desmos calcultor生成可视化的图形公式了:

max(abs(x), abs(y)) - 2 = 0

复制上面的代码,然后把它粘贴到Desmos calculator中,你就能看到一个半径为2的正方形,它的中心点在原点(0,0)上。

你也可以给它设置一些偏移值:

max(abs(x - offsetX), abs(y - offsetY)) - r = 0

offsetX = how much to move the center of the square in the x-axis
offsetY = how much to move the center of the square in the y-axis

使用像素着色器绘制一个正方形的步骤和我们在上一节教程中讲到的绘制一个圆的步骤十分相似,我们给正方形构建一个指定的函数:

  vec3 sdfSquare(vec2 uv, float size, vec2 offset) {
float x = uv.x - offset.x;
float y = uv.y - offset.y;
float d = max(abs(x), abs(y)) - size; return d > 0. ? vec3(1.) : vec3(1., 0., 0.);
} void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy; // <0, 1>
uv -= 0.5; // <-0.5,0.5>
uv.x *= iResolution.x/iResolution.y; // fix aspect ratio vec2 offset = vec2(0.0, 0.0); vec3 col = sdfSquare(uv, 0.2, offset); // Output to screen
fragColor = vec4(col,1.0);
}

哇!我们得到了一个红色正方形 !

让正方形转起来

使用下面的表示法构建一个旋转矩阵,就可以让我们的图形转起来:

矩阵帮助我们处理线性方程线性变换。实际上,一个旋转矩阵就是仿射变换矩阵的一种类型。我们可以使用矩阵来处理其他的放射变换例如:剪切、变形或者反射。

提示: 如果你想要试试矩阵计算,你可以使用Demos Matrix Calculator 或者 WolframAlpha。如果你要复习一下矩阵知识,你可以观看Derek Banas 在YouTuBe上发布的这个神奇的视频

你可以使用我在Desmos上创建的一个可视化旋转图形。我用旋转矩阵创建了一系列的线性方程式,在这些方程式中都使用旋转矩阵。

用旋转矩阵乘以WolfraAlpha中计算后得出的向量[x,y]就能得到线性方程式。其结果就是结果就是一个转换了X和Y坐标的等式。

在Shadertoy中,我们只需要关系旋转矩阵,而不是线性方程式。我也只会在讨论在Desmos中展示旋转示例的时候提及到它。

我们创建一个旋转矩阵函数,这个矩阵接受UV坐标以及一个角度用来旋转正方形。它的返回结果是一个乘以UV坐标后的旋转矩阵。然后,我们在sdfSquare函数中调用rotate函数,给它传递我们的XY坐标,或者一个它的偏移值(如果它存在的话)。我们将使用到iTime作为角度,这样正方形就能动起来。

  vec2 rotate(vec2 uv, float th) {
return mat2(cos(th), sin(th), -sin(th), cos(th)) * uv;
} vec3 sdfSquare(vec2 uv, float size, vec2 offset) {
float x = uv.x - offset.x;
float y = uv.y - offset.y;
vec2 rotated = rotate(vec2(x,y), iTime);
float d = max(abs(rotated.x), abs(rotated.y)) - size; return d > 0. ? vec3(1.) : vec3(1., 0., 0.);
} void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy; // <0, 1>
uv -= 0.5; // <-0.5,0.5>
uv.x *= iResolution.x/iResolution.y; // fix aspect ratio vec2 offset = vec2(0.0, 0.0); vec3 col = sdfSquare(uv, 0.2, offset); // Output to screen
fragColor = vec4(col,1.0);
}

请注意我们是如何在Shadertoy中定义矩阵的。让我们更近地观察旋转函数吧。

  vec2 rotate(vec2 uv, float th) {
return mat2(cos(th), sin(th), -sin(th), cos(th)) * uv;
}

根据维基百科上的GLSL规范,我们定义一个由逗号分隔各个值的矩阵,我们需要先看列。因为这个矩阵是mat2类型的,它就是一个2*2的矩阵。前面的两个值表示为第一列,后面两个值表示第二列。在WolframAlpha这种工具中,你需要首先插入行值让后用方括号分隔它们。在使用度量的时候一定要记住这点。

我们的rotate函数返回一个vec2类型的向量,因为2乘2的矩阵(mat2)乘以一个vec2的向量返回的是另外一个vec2向量。

运行以上代码,我们就可以看到一个沿着顺时钟旋转的正方形了。

总结

在这节课中,我们学会了如何绘制一个圆并且使用变形矩阵让它动起来。运用你本节和上一节课学习到的知识,使用一个SDF公式绘制出你自己的2D图形吧!

在下一篇文章中,我将会讨论如何在画布上绘制多种形状,同时也会讨论如何改变背景颜色。

资源

Shadertoy 教程 Part 3 - 矩形和旋转的更多相关文章

  1. HDU 5251 矩形面积 (旋转卡壳)

    2015年百度之星程序设计大赛 - 初赛(1) 1006 比赛链接:2015年百度之星程序设计大赛 - 初赛(1) 题目链接:HDU 5251 Problem Description 小度熊有一个桌面 ...

  2. Shadertoy 教程 Part 2 - 圆和动画

    Note: This series blog was translated from Nathan Vaughn's Shaders Language Tutorial and has been au ...

  3. NeHe OpenGL教程 第四课:旋转

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  4. BC 2015在百度之星程序设计大赛 - 预赛(1)(矩形区域-旋转卡)

    矩形区域 Accepts: 717 Submissions: 1619 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 ...

  5. BZOJ 1185: [HNOI2007]最小矩形覆盖 [旋转卡壳]

    1185: [HNOI2007]最小矩形覆盖 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 1435  Solve ...

  6. [hdu5251]矩形面积 旋转卡壳求最小矩形覆盖

    旋转卡壳求最小矩形覆盖的模板题. 因为最小矩形必定与凸包的一条边平行,则枚举凸包的边,通过旋转卡壳的思想去找到其他3个点,构成矩形,求出最小面积即可. #include<cstdio> # ...

  7. bzoj 1185 [HNOI2007]最小矩形覆盖——旋转卡壳

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1185 矩形一定贴着凸包的一条边.不过只是感觉这样. 枚举一条边,对面的点就是正常的旋转卡壳. ...

  8. BZOJ 1185: [HNOI2007]最小矩形覆盖-旋转卡壳法求点集最小外接矩形(面积)并输出四个顶点坐标-备忘板子

    来源:旋转卡壳法求点集最小外接矩形(面积)并输出四个顶点坐标 BZOJ又崩了,直接贴一下人家的代码. 代码: #include"stdio.h" #include"str ...

  9. bzoj1185 [HNOI2007]最小矩形覆盖 旋转卡壳求凸包

    [HNOI2007]最小矩形覆盖 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 2081  Solved: 920 ...

随机推荐

  1. GoLang设计模式07 - 责任链模式

    责任链模式是一种行为型设计模式.在这种模式中,会为请求创建一条由多个Handler组成的链路.每一个进入的请求,都会经过这条链路.这条链路上的Handler可以选择如下操作: 处理请求或跳过处理 决定 ...

  2. markdown写作系统

    markdown 电脑本地使用typora,可保存为md文件直接上传到有道云笔记中 直接使用博客园做为图床,可以存为草稿,然后把复制连接 有道云笔记 粘贴博客园的图片连接

  3. 深入理解netty---从偶现宕机看netty流量控制

    一.业务背景 目前移动端的使用场景中会用到大量的消息推送,push消息可以帮助运营人员更高效地实现运营目标(比如给用户推送营销活动或者提醒APP新功能). 对于推送系统来说需要具备以下两个特性: 消息 ...

  4. JVM-对象的实例化,内存布局与访问定位

    1.对象的实例化 提到对象的实例化,我们可能会想到几个问题.对象在JVM中是怎么存储的?对象里面有什么?接下来,我们就来探讨一下对象的实例化以及回答一下这两个问题. 首先我们用图例来说明对象的实例化: ...

  5. JVM探针与字节码技术

    JVM探针是自jdk1.5以来,由虚拟机提供的一套监控类加载器和符合虚拟机规范的代理接口,结合字节码指令能够让开发者实现无侵入的监控功能.如:监控生产环境中的函数调用情况或动态增加日志输出等等.虽然在 ...

  6. 网络基础--简单理解什么是DNS? TCP? UDP? Http? Socket?

    什么是IP 协议?  协议就是为了实现网络通信而创建的一系列规范.  通常我们的网络模型从上到下共分为4层: 应用层, 传输层, 网络层 和数据链路层. IP协议属于网络层协议,它精确定义了网络通信中 ...

  7. JVM学习笔记——GC算法

    GC 算法 GC 即 Garbage Collection 垃圾回收.JVM 中的 GC 99%发生在堆中,而 Java 堆中采用的垃圾回收机制为分代收集算法.即将堆分为新生代和老年代,根据不同的区域 ...

  8. hdu4479 (数学题)(算术基本定理)

    题目大意 给定一个三元组\((x,y,z)\)的\(gcd\)和\(lcm\),求可能的三元组的数量是多少,其中三元组是的具有顺序的 其中\(gcd\)和\(lcm\)都是32位整数范围之内 由算术基 ...

  9. 【UE4 C++】抛物线路径、发射轨道相关

    基于UGameplayStatics Blueprint_PredictProjectilePath_ByObjectType 根据 Object Type,算出抛物线的点集合和检测结果 static ...

  10. 【数据结构与算法Python版学习笔记】基本数据结构——列表 List,链表实现

    无序表链表 定义 一种数据项按照相对位置存放的数据集 抽象数据类型无序列表 UnorderedList 方法 list() 创建一个新的空列表.它不需要参数,而返回一个空列表. add(item) 将 ...