过火

再度出击!这次我们要玩得更火一点---把静帧变动画。没错,将棋盘格动起来!看一下效果:

这是一个经典的无限偏移动画,在很多2d横版射击游戏中都会采用的技术。如何在Processing中实现,有两种比较常见的方法。1.使用相机补位式 2.纹理采样式

1.相机补位式

( gif 取自 https://www.gameres.com/840857.html

简单地说是使用几张图片素材有机整合在一起,通过视口的偏移量来计算是否需要重置相应图片素材的位置,如果到了边缘临界,那么相关图片需要重置其位置。这样的补位方式才保证了视口里不被穿帮,观众看到的是一个连续的形象和世界。这种方式简单易懂,操控对象直接,方便调控,但是对于目前我们的代码是不合适的,因为整个代码是按照面向过程的思路去编写的,没有对象概念,对其参数不可单独控制。

2.纹理采样式

很明显是使用纹理贴图的UV信息作偏移,因为在三维渲染中,材质UV纹理包裹形式是不同种类的,常见的有:Clamp(拉伸)、Repeat(循环重复\平铺)、Mirror(镜像)。这些方式的计算过程已经被封装在OpenGL或是DircectX应用标准中,只需要在应用时定义好相关属性方可使用,讨巧的技术。

这一次先上代码:

  1. PShader shader ;
  2. int increW;
  3. int increH;
  4. int WCOUNT = 10;
  5. int HCOUNT = 10;
  6. void drawRect(PGraphics pg, int c, int x, int y, int w, int h) {
  7. pg.noStroke();
  8. pg.fill(c);
  9. pg.rect(x, y, w, h);
  10. }
  11. PGraphics drawOneGraphics()
  12. {
  13. PGraphics pg = createGraphics(width, height);
  14. pg.beginDraw();
  15. int k = 0;
  16. int c = 0;
  17. for (int x = 0; x < width; x += increW)
  18. {
  19. for (int y = 0; y < height; y += increH)
  20. {
  21. if (k % 2 == 0)
  22. c = color(255);
  23. else
  24. c = color(0);
  25. drawRect(pg, c, x, y, increW, increH);
  26. k++;
  27. }
  28. k++;
  29. }
  30. //pg.stroke(255,0,0);
  31. //pg.strokeWeight(10);
  32. //pg.noFill();
  33. //pg.rect(0,0,width,height);
  34. pg.endDraw();
  35. return pg;
  36. }
  37. void settings() {
  38. size(400, 400,P2D);
  39. }
  40. void setup() {
  41. textureWrap(REPEAT);
  42. increW = width/WCOUNT;
  43. increH = height/HCOUNT;
  44. shader = loadShader("shader.frag");
  45. shader.set("resolution", float(width), float(height));
  46. }
  47. void draw() {
  48. shader.set("time", millis()/1000.);
  49. PImage chessboard = drawOneGraphics();
  50. image(chessboard, 0,0);
  51. filter(shader);
  52. }

可以看到,这一次我再一次将绘制棋盘格过程进行了封装,变成了drawOneGraphics()这一函数。因为要使用纹理偏移,Processing默认的渲染框架是JAVA2D,不满足需求,因此要修改为P2D,在size()函数中添加参数P2D。其次是将着色器PShader导进来,我们要使用它作为纹理着色器为图像着色(其实就是纹理的偏移操作)。Processing着色器是需要通过filter()shader()调用的,前者是作为过滤器,即texture shader纹理着色使用,一般做一些后期处理,而后者是传统的matrial shader材质着色,用来作用于场景中的模型上。所以大家以后看Processing opengl渲染的代码,经常会看到PShader被用在 filter()shader()函数中,留意好不同用法。纹理着色是要放在filter()中的,一般这样的调用顺序:

  1. image(mygraphics1,0,0,);
  2. image(mygraphics2,0,0,);
  3. filter(myshader); //注意是放在图像绘制好之后调用,作为后期处理,当然也可以理解为 为我们视口大小的面片上着色

而材质着色是要放在shader()中的,调用顺序如下:

  1. shader(myshader);
  2. image(mygraphics1,0,0,);
  3. shader(myshader2)
  4. drawMyGeometry(); //注意是放在绘制模型之前调用,作为材质着色器使用

当然有时候你的shader都可以放在任意一个函数中使用,只要调用顺序不要搞错。接下来看看这回的shader源码:

  1. #ifdef GL_ES //这一部分是基于平台的数值精度定义,方便优化
  2. precision mediump float;
  3. precision mediump int;
  4. #endif
  5. #define PROCESSING_TEXTURE_SHADER //宏定义为Processing 纹理shader,不写无妨
  6. uniform sampler2D texture; //等待pde中的默认graphics传入,Processing底层封装好的变量名,不能更改,如果更改名字就意味用户自己的贴图变量
  7. uniform float time; //等待被传入的时间变量
  8. uniform vec2 resolution; //视口大小
  9. varying vec4 vertColor;
  10. varying vec4 vertTexCoord;
  11. void main(void) {
  12. vec2 p = vec2(time*0.1,time*0.1) + gl_FragCoord.xy / resolution.xy ; //待采样的目标纹理坐标
  13. vec3 col = texture2D(texture, p).xyz;//纹理采样
  14. vec4 cc = vec4( col, 1.0) ;
  15. gl_FragColor = cc; //输出片元颜色
  16. }

有些说明我放在了源码注释中了。其实glsl的学习是要经过漫长的适应期的,因为其并行的计算方式和我们解决问题的思考方式是不同的。网上有很多学习的资源,我推荐一个:https://thebookofshaders.com/ 这个网站专门供同学学习shader,并且提供了很多实时编辑预览的工具,很酷~~~在我们的例子中是使用texture shader。要提的是texture2D()这个函数,2维纹理采样,将纹理图像通过相应坐标取值,取每一像素的颜色,返回给我们视口中的像素值,也就是gl_FragColor,那如何知道哪个纹理上的值是对应屏幕上的哪个点呢,使用gl_FragCoord.xy / resolution.xy来计算UV,gl_FragCoord表示当前片元着色器处理的候选片元窗口相对坐标信息,resolution是我们的视窗大小二维向量信息,一般的纹理着色filter,UV是标准的[0,1]相对坐标值。如果要偏移纹理图像,那么就得在texture2D()第二个参数上下功夫,将其vec2向量偏移一个值。vec2 p = vec2(time*0.1,time*0.1) + gl_FragCoord.xy / resolution.xy ;在这里,偏移了vec2(time*0.1,time*0.1)的向量值,把标准UV值和它相加,这样,最终的效果会是纹理图像在视口中朝着斜45度角偏移。当然读者可以尝试不同的角度和速度。

注意到没有,有些shader属性是需要外部传进去的,如resolution、time。在pde中需要使用PShader的set()函数进行传参。resolution就是视口大小,time,让其形成动画的因子,不断地提高偏移向量值,因为Processing环境的millis()是毫秒级计时,需要除以1000来保证shader中时间概念的一致性。如果读者还有问题,请留言。

回火

是不是还能再改改,做成不同效果呢。把棋盘格简化成一色的,然后各自之间留些空隙会比较好看。先绘制静帧:

其实这里是有细节要提的,因为留了空隙所以务必计算好留多大,况且要做纹理偏移,大小如果一样合不合情。答案是否定的。如下图是正确的做法:

原因是纹理偏移,上一张的缝隙大小会被后一张所承接,因此边界处的缝隙量保持0.5个单位才能和里头的1.0相一致。在计算过程中可以假设边缘处的距离0.5y,非边缘处的空隙为y,每方块大小x,如果以画面33的规格绘制,窗口大小为400400,那么一轴向上的总像素量为 X = 3*x + 2*y + 0.5*y*2.经计算,得到400 = 3*x + 3*y ,其中x就是increX,那么就很容易得出 空隙 y = 视窗宽度 / 方块个数 - 步长increment。化成代码如下:

  1. int edageweight = 10;
  2. int WCOUNT = 8;
  3. int HCOUNT = 8;
  4. int increW;
  5. int increH;
  6. int k = 0;
  7. int i, j;
  8. i = -(increW+edageweight); //变量i辅助计算绘制起始点
  9. j = -(increH+edageweight); //变量j辅助计算绘制起始点
  10. for (int x=0; x < 10; x++) {
  11. i += increW+edageweight;
  12. for (int y=0; y < 10; y++) {
  13. j += increH+edageweight;
  14. if (k % 2 == 0)
  15. {
  16. int c = color(200, 20, 20);
  17. drawRect(c, i+edageweight/2, j+edageweight/2, increW, increH);
  18. }
  19. k++;
  20. }
  21. j = -(increH+edageweight);
  22. k++;
  23. }
  24. i = -(increW+edageweight);

如果用上之前的纹理着色器,那么会有下面的效果:

pde代码:

  1. int WCOUNT = 8;
  2. int HCOUNT = 8;
  3. int increW;
  4. int increH;
  5. int edageweight = 10;
  6. PShader shader ;
  7. void settings() {
  8. size(400,400, P2D);
  9. }
  10. void setup() {
  11. textureWrap(REPEAT);
  12. shader = loadShader("shader.frag");
  13. shader.set("resolution", float(width), float(height));
  14. shader.set("time", millis()/1000.);
  15. increW = (width)/WCOUNT-edageweight;
  16. increH = (height)/HCOUNT-edageweight;
  17. }
  18. void draw() {
  19. shader.set("time", millis()/1000.);
  20. background(230);
  21. Process();
  22. filter(shader);
  23. }
  24. void drawRect(int c, int x, int y, int w, int h) {
  25. noStroke();
  26. fill(c);
  27. rect(x, y, w, h);
  28. }
  29. void Process()
  30. {
  31. int k = 0;
  32. int i, j;
  33. i = -(increW+edageweight);
  34. j = -(increH+edageweight);
  35. for (int x=0; x < 10; x++) {
  36. i += increW+edageweight;
  37. for (int y=0; y < 10; y++) {
  38. j += increH+edageweight;
  39. if (k % 2 == 0)
  40. {
  41. int c = color(200, 20, 20);
  42. drawRect(c, i+edageweight/2, j+edageweight/2, increW, increH);
  43. }
  44. k++;
  45. }
  46. j = -(increH+edageweight);
  47. k++;
  48. }
  49. i = -(increW+edageweight);
  50. }

还想加上鼠标交互?可以啊,加一个mouse 二维向量吧,shader代码如下:

  1. #ifdef GL_ES
  2. precision mediump float;
  3. precision mediump int;
  4. #endif
  5. #define PROCESSING_TEXTURE_SHADER
  6. uniform sampler2D texture;
  7. uniform float time;
  8. uniform vec2 resolution;
  9. uniform vec2 mouse;
  10. varying vec4 vertColor;
  11. varying vec4 vertTexCoord;
  12. void main(void) {
  13. vec2 p = vec2(1,-1)*mouse.xy/resolution.xy + gl_FragCoord.xy / resolution.xy ;
  14. vec3 col = texture2D(texture, p).xyz;
  15. vec4 cc = vec4( col, 1.0) ;
  16. gl_FragColor = cc;
  17. }

pde中加入:

  1. shader.set("mouse", (float)mouseX, (float)mouseY);

尾声

是时候做个总结了,使用Processing绘制一些基础纹理,然后用上shader帮其着色,做一些素材供其他软件使用再造,何尝不是一件很时髦的工作流程。今后,笔者还会使用这种工作流做一些其他工作,敬请期待,谢谢。

Processing 网格(棋盘格)无限偏移纹理动画的更多相关文章

  1. Unity3D 给模型偏移纹理

    给模型偏移纹理 using UnityEngine; using System.Collections; [RequireComponent(typeof(Renderer))] public cla ...

  2. unity3d 纹理动画

    不知道大家有没有玩过赛车游戏 赛车游戏的跑道有路标,如下图 玩过赛车游戏的都知道,大多数赛车游戏的路标是会动的,如上图,它会从右往左运动 不会发动态图,大家脑补一下吧 没有玩过赛车游戏的也不要紧,大家 ...

  3. Processing 网格纹理制作(棋盘格)

    写在前面的话 很久没有写博文了.最近在整理Processing有关文档,看到之前做的一些例子,想着分享在互联网上,当然和以前一样,目前也仅为了给初学者有个学习参考,笔者能力有限.废话不多说,干就完事了 ...

  4. Processing 网格纹理制作(棋盘格)使用pixel() set()像素点绘制方式

    接上 我们趁热打铁,紧接上一回的棋盘格绘制,来挖掘一些不同绘制思路,使用pixel()函数来绘画.这是一个以每个像素点作为对象来绘制的思路,而不是以图形的方式来填充.这就改变了绘画思路.实际上,Pro ...

  5. three.js 纹理动画实现

    需求: 1.使用一张长图.分别播放这张长图的不同位置 来达到动态内容的目的 解决方案: 1.纹理创建并指定重复方向:this.texture.wrapS = this.texture.wrapT = ...

  6. cocos2d-x 网格动画深入分析

    转自:http://www.2cto.com/kf/201212/179828.html 在TestCpp中的EffectsTest示例中展示了一些屏幕特效,它是将屏幕划分为多个格子,并对这些格子进行 ...

  7. UE4编程之C++创建一个FPS工程(二)角色网格、动画、HUD、子弹类

    转自:http://blog.csdn.net/u011707076/article/details/44243103 紧接上回,本篇文章将和大家一同整理总结UE4关于角色网格.动画.子弹类和HUD的 ...

  8. UE4的编程C++创建一个FPSproject(两)角色网格、动画、HUD、子弹类

    立即归还,本文将总结所有这些整理UE4有关角色的网络格.动画.子弹类HUD一个简单的实现. (五)角色加入网格 Character类为我们默认创建了一个SkeletaMeshComponent组件,所 ...

  9. Unity3D ShaderLab 模拟纹理运动

    Unity3D ShaderLab 模拟纹理运动 这一篇,我们要说到着色器上的uv贴图的滚动效果,这样的场景可以用在河流,瀑布,熔岩等效果.算是创建纹理动画的基础技术之一. 所以 准备一个新的着色器文 ...

随机推荐

  1. 更改默认Xcode

    更改默认 Xcode 的指令: sudo xcode-select -switch /Applications/Xcode.app  如果系统里面有好几个版本的 Xcode,可以用这个命令指定默认的 ...

  2. 解决SpringBoot jar包中的文件读取问题

    前言 SpringBoot微服务已成为业界主流,从开发到部署都非常省时省力,但是最近小明开发时遇到一个问题:在代码中读取资源文件(比如word文档.导出模版等),本地开发时可以正常读取 ,但是,当我们 ...

  3. Mac搭建appium环境

    1.安装brew 查看是否已经装上brew,终端输入命令:brew --version,已经装上的就不用再装了: 如果没有安装,终端输入命令:ruby -e "$(curl -fsSL ht ...

  4. [ASP.NET Core开发实战]开篇词

    前言 本系列课程文章主要是学习官方文档,再输出自己学习心得,希望对你有所帮助. 课程大纲 本系列课程主要分为三个部分:基础篇.实战篇和部署篇. 希望通过本系列课程,能让大家初步掌握使用ASP.NET ...

  5. FileZilla Server FTP服务器失败

    使用Filezilla Server配置FTP服务器https://blog.csdn.net/chuyouyinghe/article/details/78998527 FileZilla Serv ...

  6. APM姿态控制流程

    对初学者了解控制流程有一定帮助 在主循环执行过程中(比如Pixhawk的任务调度周期2.5ms,400Hz:APM2.x为10ms,100Hz),每一个周期,程序会按下述步骤执行:• 首先,高层次文件 ...

  7. SpringBoot中JPA,返回List排序

    这里简单示例,利用query,根据“createtime”字段,进行 desc 排序,最近日期的数据在最前面. public List<StatusEvent> findAll(Speci ...

  8. ETC1

    对纹理进行Alpha通道分离的好处 https://blog.csdn.net/u011926026/article/details/53982180 拆分贴图的Alpha通道 --对抗ETC1的原罪 ...

  9. [BUUOJ记录] [GXYCTF2019]BabySQli

    有点脑洞的题,题目不难,主要考察注入和联合查询的一个小特点 进入题目是一个登录框,看看源代码,在search.php文件中发现了这个 大写的字母和数字很明显是base32,先用base32解码一下,发 ...

  10. 跟着兄弟连系统学习Linux-【day05】

    day05-20200602 p19.其他文件搜索命令 (百度搜索everything,安装,可以实现Windows秒级搜索文件)Linux中同样可以实现此功能. [locate 文件名]locate ...