【16】processing-渲染(中文)

渲染TechniquesCasey Reas和Ben Fry

 

默认情况下,所有内容都将绘制到主显示窗口。然而,有时绘制到另一个图形表面有一个优势。显示窗口中可用的所有绘图功能都可以应用于屏幕外的绘图表面,然后作为图像或纹理绘制回显示窗口。这种技术使程序更容易被想象成一堆层,类似于照片编辑和矢量绘图软件中使用的技术。类似地,处理中的绘图表面可以移动,使用混合效果和透明度绘制,并以不同的顺序绘制以更改层的组合方式。在讨论移动到多个绘图曲面之前,本章首先讨论处理使用的不同渲染器。

 

渲染器

这篇短文提供了导致这些新技术的数字印刷技术的简要历史。本文介绍了为产生打印输出而编写的软件示例,并讨论了几种常见的当代打印技术。围绕数字印刷的行业充满了商标名称和流行语,因此本文希望揭开一些术语的神秘面纱,并提供指向其他信息的指针。以下内容是为在家打印或与供应商合作制作小版本而定制的。

 

处理有三个主要渲染器:默认渲染器P3D和P2D。默认渲染器用于本书中的大多数程序;用于二维绘图。当其他渲染之一未定义为size()的第三个参数时使用。三维绘图章节(第21页)讨论了三维绘图的P3D渲染器。P2D渲染器是一种替代的2D渲染器,它比大多数任务的默认渲染器快得多,但是它牺牲了一些视觉质量来提高速度。P2D和P3D都使用了一种称为OpenGL的软件规范,该规范在许多gpu(计算机显卡上的图形处理单元)上得到支持,以加速绘图。OpenGL渲染器(P2D和P3D)的视觉质量可以使用smooth()和hint()函数进行调整。

 

默认情况下,所有程序都启用了平滑,但如果选择参数smooth(),则可以提高使用P2D和P3D的OpenGL渲染的质量,例如smooth(4)将提高抗锯齿的质量,从而提高图像质量。可选参数从低到高分别为2、4和8,默认值为2。这些选项仅在支持它们的显卡上可用,可能会减慢程序速度或需要更多内存。

 

函数的作用是:高级用户可以修改几何图形绘制到屏幕的详细信息。默认情况下,在渲染场景时,每个渲染器根据对大多数用户更有用和更有效的方式来决定绘制顺序、形状遮挡、透视失真等。如果其他决定对特定程序更好,则使用提示来更改这些默认值。有一个hint()函数,但它的参数启用和禁用一系列呈现决策。例如,要使行在透视图中显示,请编写提示(启用“笔划”透视图)并将其关闭,请编写提示(禁用“笔划”透视图)。默认情况下不会启用此功能,因为它速度较慢,并且会使主要平面场景中的线条看起来奇怪。但在绘制许多使用大型三维空间的线时,这是非常有用的。hint()的参数随时间而变化,因此请参阅当前选项的处理引用。

 

另一个绘图表面新建绘图表面的大小可以与显示窗口的大小相同,但也可以更小或更大。处理中的每个绘图面都是PGraphics类的实例。显示窗口是要绘制到的默认曲面;默认情况下,每个函数都会在其中进行渲染。例如,要在显示窗口中绘制圆,请运行 ellipse() 函数

 

 

background(0); 
ellipse(50, 50, 75, 75);

 

要将同一形状绘制到新曲面中,请首先创建一个新的PGraphics对象,然后在绘图函数的名称之前键入对象名称和点运算符来绘制该对象。这个新的曲面与image()函数一起放置在显示窗口中。

 

 

PGraphics circle = createGraphics(100, 100);
circle.beginDraw();
circle.background(0);
circle.ellipse(50, 50, 75, 75);
circle.endDraw();
image(circle, 0, 0);

 

函数的作用是:按照参数定义的像素大小生成新的绘图面。此步骤只在初始化时发生一次。beginDraw()函数用于开始处理自定义PGraphics对象,endDraw()方法用于停止。每次进行更改时都需要这两种方法。最后,可以使用image()函数将曲面绘制到显示窗口。控制位置和大小的image()的所有参数都可用于更改曲面的绘制方式。

 

在实践中,新的PGraphics层通常与具有setup()和draw()的程序一起使用。为了协同工作,PGraphics对象被声明为全局对象,它在setup()中创建,然后可以在draw()中修改。

 

 

PGraphics circle;
void setup() {
  size(100, 100);
  circle = createGraphics(100, 100);
}
void draw() {
  circle.beginDraw();
  circle.background(0);
  circle.ellipse(50, 50, 75, 75);
  circle.endDraw();
  image(circle, 0, 0);
}

 

基于此示例,与使用PGraphics对象最明显的区别是附加的代码行和在每个绘图函数前面附加对象名称的额外步骤。这个例子被简化以演示技术的机制,但是它没有做任何事情来证明它是正确的。下面的示例揭示了创建新绘图曲面可以解决的典型问题。该程序的目标是使用十字线来显示光标的位置,并在每次单击鼠标时在屏幕上绘制一个新的圆。对于要在屏幕上累积的圆,不使用background()函数。这会产生意外的效果,如代码中所示,还会累积十字准线:

 

  

 

void setup() {
  size(100, 100);
  background(0);
  noCursor();
}
void draw() {
  stroke(255);
  line(mouseX, 0, mouseX, height);
  line(0, mouseY, width, mouseY); 
}
void mousePressed() {
  noStroke();
  fill(255, 100);
  ellipse(mouseX, mouseY, 40, 40);
}

 

为了允许圆在显示窗口中累积,但对十字光标有不同的效果,将创建一个新的绘图曲面来渲染圆,而十字光标则在主显示窗口中绘制。带有圆的曲面在draw()中的每一帧中都会被重绘,然后是线,因此它们位于圆层的顶部。

 

 

PGraphics circles;
void setup() {
  size(100, 100);
  circles = createGraphics(width, height); circles.beginDraw();
  circles.background(0);
  circles.noStroke();
  circles.fill(255, 100); circles.endDraw();
  noCursor();
} 
void draw() {
  image(circles, 0, 0);
  stroke(255);
  line(mouseX, 0, mouseX, height);
  line(0, mouseY, width, mouseY);
}
void mousePressed() {
  circles.beginDraw();
  circles.ellipse(mouseX, mouseY, 40, 40);
  circles.endDraw();
}

 

为了允许圆在显示窗口中累积,但对十字光标有不同的效果,将创建一个新的绘图曲面来渲染圆,而十字光标则在主显示窗口中绘制。带有圆的曲面在draw()中的每一帧中都会被重绘,然后是线,因此它们位于圆层的顶部。

 

本例中的setup()定义了新绘图曲面的默认值。背景设置为黑色,填充颜色设置为透明白色,笔划关闭。这些属性适用于整个程序的“圆”层,因此每次在mousePressed()中绘制圆时,它都不使用笔划,而是使用透明的白色绘制。

 

使用PGraphics对象的另一个很好的原因是只渲染一次几何体,但在程序中多次将其显示为纹理单元。下面的示例在一个仅为50×50像素的曲面上的随机位置绘制一个椭圆,然后通过将image()函数放置在嵌套循环中,在网格中绘制该曲面。

 

 

PGraphics tile;
void setup() {
  size(700, 100);
  tile = createGraphics(50, 50);
  background(0);
}
void draw() {
  runTile();
  for (int y = 0; y < height; y += tile.height) {
    for (int x = 0; x < width; x += tile.width) {
      image(tile, x, y);
      }
   }
}
void runTile() {
  float x = random(20, tile.width-20);
  float y = random(20, tile.height-20);
  tile.beginDraw();
  tile.noStroke();
  tile.fill(0, 20);
  tile.rect(0, 0, tile.width, tile.height);
  tile.fill(255);
  tile.ellipse(x, y, 10, 10);
  tile.endDraw();
}

 

这个例子阐明了使用PGraphics对象的两个方面。首先,每个对象都有自己的宽度和高度字段,就像主显示窗口一样。其次,当noStroke()和rect()等绘图函数通过点运算符应用于绘图表面时,变量仍然属于主程序。注意runTile()函数中的x和y变量没有定义为属于tile对象。

 

OpenGL surfaces正如size()函数定义程序的主渲染器一样,每个createGraphics()函数使用第三个可选参数定义PGraphics对象的渲染器。当不使用第三个参数时(如本章中的示例所示),将设置默认渲染器。P2D和P3D渲染器是其他选项。例如,要为三维形状创建新的绘图曲面,请使用P3D作为size()和createGraphics()的第三个参数:

 

 

PGraphics cube;
void setup() {
  size(100, 100, P3D);
  cube = createGraphics(width, height, P3D);
}
void draw() {
  background(0); 
  drawCube();
  image(cube, 0, 0);
}
void drawCube() { 
  cube.beginDraw();
  cube.lights();
  cube.background(0);
  cube.noStroke();
  cube.translate(width/2, height/2);
  cube.rotateX(frameCount/100.0);
  cube.rotateY(frameCount/200.0);
  cube.box(40);
  cube.endDraw();
}

 

正如前一个示例所暗示的,为新图形曲面选择渲染器时要记住的唯一规则是匹配在size()和createGraphics()中定义的渲染器。此规则的例外情况是,当P3D是在size()中定义的主渲染器时,使用默认渲染器和P2D作为备用选项。例如,以下代码将不会运行,因为当默认渲染器用作主渲染器时,无法运行三维渲染器:

 

PGraphics cube;
void setup() {
    size(100, 100);
    cube = createGraphics(width, height, P3D); // Error
}
void draw() {
    cube.beginDraw();
    cube.box(40);
    cube.endDraw();
    image(cube, 0, 0);
}

 

组合曲面本章的最后一节演示在显示窗口中组合多个绘图曲面的技术。这是在本章前面的代码基础上构建的,但进一步扩展了它。下一个例子基于代码35-07,有两个更改。首先,添加另一个绘图表面,然后,从每个层中删除background(),并替换为clear()。clear()方法类似于定义背景,因为它清除了绘图表面中的所有内容,但它使每个像素都透明,而不是用颜色填充每个像素。这里,clear()用于更容易将两个绘图曲面组合在一起,因为绘制到每个缓冲区中的形状由透明像素包围。显示窗口(默认的PGraphics对象)不能使用透明背景。它们是创建新绘图曲面的主要优点之一。

 

 

PGraphics cubeA;
PGraphics cubeB;
void setup() {
  size(200, 200, P3D); 
  cubeA = createGraphics(width, height, P3D);
  cubeB = createGraphics(width, height, P3D);
} 
void draw() {
  background(0);
  drawCubeA();
  drawCubeB();
  float alphaA = map(mouseX, 0, width, 0, 255);
  float alphaB = map(mouseY, 0, height, 0, 255);
  tint(255, alphaA);
  image(cubeA, 0, 0);
  tint(255, alphaB);
  image(cubeB, 0, 0);
}
void drawCubeA() {
  cubeA.beginDraw();
  cubeA.lights();
  cubeA.clear();
  cubeA.noStroke();
  cubeA.translate(width/2, height/2);
  cubeA.rotateX(frameCount/100.0);
  cubeA.rotateY(frameCount/200.0);
  cubeA.box(80);
  cubeA.endDraw();
}
void drawCubeB() {
  cubeB.beginDraw();
  cubeB.lights();
  cubeB.clear();
  cubeB.noStroke();
  cubeB.translate(width/2, height/2);
  cubeB.rotateX(frameCount/150.0);
  cubeB.rotateY(frameCount/250.0);
  cubeB.box(80);
  cubeB.endDraw();
}

 

在显示窗口中移动光标以修改每个层的透明度。彼此的可见性由image()函数前面的tint()设置。快速查看drawCubeA()和drawCubeB()函数可以发现编写程序的更好方法。这两个函数几乎相同,但有三个不同点:要绘制的曲面的名称、x轴的旋转量、y轴的旋转量。这些值可以作为参数传递到函数中,这样就可以用一个函数代替两个函数。未知语法是如何将PGraphics对象作为参数传递给函数。幸运的是,它遵循与所有参数相同的模式,数据类型的名称后面跟着一个变量名。更新后的程序如下。

 

 

PGraphics cubeA;
PGraphics cubeB;
void setup() {
  size(200, 200, P3D); 
  cubeA = createGraphics(width, height, P3D);
  cubeB = createGraphics(width, height, P3D);
} 
void draw() {
  background(0);
  drawCube(cubeA, 100, 200);
  drawCube(cubeB, 150, 250); 
  float alphaA = map(mouseX, 0, width, 0, 255);
  float alphaB = map(mouseY, 0, height, 0, 255);
  tint(255, alphaA);
  image(cubeA, 0, 0);
  tint(255, alphaB);
  image(cubeB, 0, 0);
}
void drawCube(PGraphics cube, float xd, float yd) {
  cube.beginDraw();
  cube.lights();
  cube.clear();
  cube.noStroke();
  cube.translate(cube.width/2, cube.height/2);
  cube.rotateX(frameCount/xd);
  cube.rotateY(frameCount/yd);
  cube.box(80);
  cube.endDraw(); 
}

 

当cubeA对象被传递到draw()第二行的drawCube()函数中时,drawCube()函数将呈现到该对象中。当cubeB对象被传递到draw()第三行的drawCube()函数中时,drawCube()函数将呈现到该对象中。这种能力是修改后的程序的强大功能;它展示了通过参数化绘制到任何PGraphics对象中的模块化技术。

下一个示例与上一个示例类似,但它将两个绘图曲面与blendMode()函数混合在一起。混合模式在setup()结束时定义,然后在程序期间应用。

 

 

 

PGraphics cubeA;
PGraphics cubeB;
void setup() {
  size(200, 200, P3D); 
  cubeA = createGraphics(width, height, P3D);
  cubeB = createGraphics(width, height, P3D);
  blendMode(DARKEST);
} 
void draw() {
  background(255);
  drawCube(cubeA, 100, 200);
  drawCube(cubeB, 150, 250); 
  image(cubeA, 0, 0);
  image(cubeB, 0, 0);
}
void drawCube(PGraphics cube, float xd, float yd) {
  cube.beginDraw();
  cube.lights();
  cube.clear();
  cube.noStroke();
  cube.translate(cube.width/2, cube.height/2);
  cube.rotateX(frameCount/xd);
  cube.rotateY(frameCount/yd);
  cube.box(80);
  cube.endDraw();
}

 

通常,混合模式和使用tint()控制透明度是在主显示窗口中轻松混合两个PGraphics对象的像素的主要方法。试着用本章最后一个例子来进行实验。请记住,默认的混合模式是blend mode(blend),可以重置为程序绘制的方式。另外,有些混合模式不适用于所有背景值。例如,如果背景是白色的,那么使用blendMode(LIGHTEST)将始终是白色屏幕,因为没有像素会比背景值更亮。相反,blendMode(最暗)和blendMode(相乘)没有黑色背景的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值