OpenGL(LibGDX)激光束

来源:互联网 发布:眼睛变大知乎 编辑:程序博客网 时间:2024/06/07 04:55

几周前,我开始考虑如何在我们的游戏原型防御中集成一些不错的激光fx。

在互联网上搜索一些关于这个问题的指南后,我在开发者的博客上发现了一个单独的帖子; Cliffski的。

不过,令人遗憾的是,他只是就这个问题提出了一个大纲,没有任何真正的实施或细节。

在实施他的想法之后,我开始试图加强和扩大它; 这就是我这样做的。

最终结果
在游戏中

Pro Tip#1:无论何时使用OpenGL,设计效果; 学习使用所需IDE的热插拔功能。例如; 代码被整合到正在运行的程序中,每当你保存它; 而无需重新启动整个程序。这样可以轻松尝试不同的混合技术等。

激光FX 101

要在游戏中创建激光fx,您基本上需要3个精灵:

  1. 开始帽背景; 来自喷嘴的初始爆炸
  2. 中间部分:  激光器的重复部分
  3. 端盖: 您的激光的最后部分。(例如淡出)
  4. 干扰叠加:可重复的重叠动画,显示“干涉”,将给出方向和现实主义。

为了我们的目的,我们将通过另外3个精灵来扩展它,并调整其他4。

本质上我将开始,结束和中间部分划分为2个精灵; 背景和叠加。

背景表示激光的特征“发光”,而叠加表示“白色,明亮”的光束。

我做到了这一点,所以我可以通过编程方式设置这个sprite的背景颜色(发光),使我能够减少纹理所需的量。(并使我能够在游戏过程中改变颜色以获得更多的古怪效果)

所以现在我们有7个精灵:

  1. 开始上限
    1. 背景: 来自喷嘴的爆发和辉光
    2. 覆盖层:来自喷嘴的爆破和梁
  2. 封顶
    1. 背景: 结束部分发光; 淡出了光辉
    2. 叠加: 结束部分梁; 从光束中淡出
  3. 中段
    1. 背景: 光束的光芒
    2. 叠加: 梁本身

中段

(作者笔记:px测量是64x64的图像) 

中间背景

 

中间重叠

由于中间部分将定义激光器的实际外观,我们将从此开始。打开您最喜欢的作品程序,并图像中间创建一条白色线条,从上到下宽度为2 像素宽的单独图层,我们称之为“中间叠加层”现在添加一个外部发光效果到这个层,白色,它作为一个非线性淡入淡出,使它以5 像素结束“覆盖”复制到新层中,称之为“中间背景”。现在调整外部发光效果,使其淡出更大(〜18像素),并具有透明度50%现在将叠加层和背景图层保存为单独的精灵。(例如:laser-mid-o.png&laser-mid-b.png)不要忘记将整个图像保存到单独的文件,因为我们将重新使用它的开始和结束部分。

专业提示#2:您可以添加具有不同外部辉光效果的重叠/背景图层,以创建不同类型的激光。请记住,背景图层应该表示“发光”,而覆盖层应该代表光束本身。

开始部分

开始背景

 

开始叠加

“mid-overlay”“mid-background”的内容复制到名为“start-overlay”“start-background”的两个新图层中

“中间重叠”图层上,禁用外部辉光,并在图像的中心绘制一个白色圆圈(或者您认为喷嘴的任何位置),稍大于“中间重叠”线。拆下喷嘴后面的一部分线,所以你最终会得到类似于一个球的东西,一个从它出来的棍子。

然后重新启用此层的辉光。

现在重复这个过程为“中后卫” ; 所以发光结合了圆圈。

将图层保存为单独的精灵。

(例如:laser-start-o.png&laser-start-b.png)

结束部分

结束背景

 

结束叠加

“中间叠加”“中间背景”的内容复制到称为“终端覆盖”“末端背景”的两个新图层中

创建一个临时图层,并从顶部到底部填充从白色到透明的渐变。选择结果填充,并使用选择删除“end- *”层中的内容。

从本质上讲,您应该从底部到顶部最终都会出现从不透明到透明的褪色效果。

再次,将图层保存为单独的精灵。

(例如:laser-end-o.png&laser-end-b.png)

Pro Tip#3:这显然可以用不同的方式完成,尝试和实验!

叠加动画

叠加动画

为此,我简单地使用随机噪声,模糊的我的光束的长度,宽度大约一半的光辉。然后我通过向上移动纹理来动画化,确保它在底部和顶部都无缝重复。

(例如:将顶部向外移动的部分复制到底部)

现在到了实际的代码!

渲染

基本上我们要渲染:

  1. 设置混合添加 ; {GL10.GL_SRC_ALPHA,GL10.GL_ONE}
  2. 调整光束的α和发光颜色(衰变效果)
  3. 对于{x =开始,中间,末尾精灵}
    1. 将颜色设置为发光颜色
    2. 绘制x- background精灵
    3. 将颜色设置为光束颜色
    4. 绘制x -overlay精灵
  4. 将颜色设置为默认值
  5. 从启动子程序的中心绘制重叠动画到最终精灵的中心。

增加的是:

  • 向alpha添加随机性
  • 添加褪色激光; 例如:中点网格(和端帽网格)的末端可以具有targetAlpha。(见底部代码)

混纺

首先,激光look'n'feel的主要部分是一个特定的混合技术呼叫加成

openGL命令是:

OpenGL的

1
openGlContext.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);

LibGDX

1
2
3
spriteBatch.end();   // actual drawing is done on end(); if we do not end, we contaminate previous rendering.
spriteBatch.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
spriteBatch.start();

区别?好看:

混合

混合在一起

颜色

现在,为您的光束和光线选择一种颜色,并确保用于绘制背景精灵的网格  具有发光颜色,因为它是顶点颜色; 并且覆盖精灵具有光束颜色。

要获得传统的激光效果,请使用红色作为发光,白色用于光束。

OpenGL的

1
2
3
4
vertices[idx++] = x;
vertices[idx++] = y;
vertices[idx++] = color;
vertices[idx++] = u;
vertices[idx++] = v;

LibGDX

1
spriteBatch.setColor(color);

为了使激光看起来更逼真,我们将添加一个简单的衰减效果,将发光光束的alpha设置为

1
color.alpha = 1- (lifeTimeOfLaser / totalTimeOfLaser) ^ 2

通过使这个指数而不是线性,我们得到一个很好的“余辉”效应。

纹理重复

对于几乎所有的纹理,我们不需要重复{开始,结束}或只是拉伸它们{middle}

主要的问题是Overlay动画。我们需要垂直重复这个纹理,所以我们不会得到任何伸展的文物。

通常这可以通过调整网格的纹理坐标中的V来轻松解决。

1
vertices[idx++] = v * scale;  // Where factor by which we are stretching

但是,仅当叠加动画处于单个纹理中时,此操作才会起作用。

如果你是(我真的希望如此;如果不使这个优先级数字1)使用纹理地图集,那么U,V坐标指向纹理内的一个区域,我们不能简单地重复。

解决这个问题的方法是创建一个由多个指向该区域的四边形组成的单个网格。

创建这样一个网格的方法:

(作者注:此方法还包括设置所述网格的颜色,以及a的扩展名可以给出α梯度。例如:网格从colorT.alpha内插到colorT.alpha * alpha的比例。)

[基于LibGDX SpriteBatch,稍作调整]

1
2
3
4
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
       /**
    * Creates a mesh which will draw a repeated texture. To be used whenever we are dealing with a region of a TextureAtlas
    * @param vertices For re-use, the vertices to use for the mesh. If insufficient are provided, a new array will be constructed
    * @param region The AtlasRegion to use
    * @param scale The factor by which we want to repeat our texture
    * @param x
    * @param y
    * @param originX
    * @param originY
    * @param width
    * @param height
    * @param scaleX Scale by which we want to expand the mesh on X
    * @param scaleY Scale by which we want to expand the mesh on Y
    * @param rotation Degrees of rotation for mesh
    * @param colorBase The initial base color
    * @param alpha The alpha by which to mult the colorBase by; resulting in the end interpolation target.
    * @return
    */
privatestatic final float[] constructEndingMesh(
        float[] vertices, AtlasRegion region, intscale, floatx,
        floaty, floatoriginX, floatoriginY, floatwidth,
        floatheight, floatscaleX, floatscaleY, floatrotation,
        Color colorT, floatalpha)
{
    if(scale * 20> vertices.length)
        vertices = newfloat[20*scale];
 
    floatcolor = colorT.toFloatBits();
    floatcolorE;
 
    intidx = 0;
 
    // bottom left and top right corner points relative to origin
    finalfloat worldOriginX = x + originX;
    finalfloat worldOriginY = y + originY;
    floatfx = -originX;
    floatfy = -originY;
    floatfx2 = width - originX;
    floatfy2 = height - originY;
 
    // scale
    if(scaleX != 1|| scaleY != 1) {
        fx *= scaleX;
        fy *= scaleY;
        fx2 *= scaleX;
        fy2 *= scaleY;
    }
 
    // construct corner points, start from top left and go counter clockwise
    finalfloat p1x = fx;
    finalfloat p1y = fy;
    finalfloat p2x = fx;
    finalfloat p2y = fy2;
    finalfloat p3x = fx2;
    finalfloat p3y = fy2;
    finalfloat p4x = fx2;
    finalfloat p4y = fy;
 
    floatFx1;
    floatFy1;
    floatFx2;
    floatFy2;
    floatFx3;
    floatFy3;
    floatFx4;
    floatFy4;
 
    // rotate
    if(rotation != 0) {
        finalfloat cos = MathUtils.cosDeg(rotation);
        finalfloat sin = MathUtils.sinDeg(rotation);
 
        Fx1 = cos * p1x - sin * p1y;
        Fy1 = sin * p1x + cos * p1y;
 
        Fx2 = cos * p2x - sin * p2y;
        Fy2 = sin * p2x + cos * p2y;
 
        Fx3 = cos * p3x - sin * p3y;
        Fy3 = sin * p3x + cos * p3y;
 
        Fx4 = Fx1 + (Fx3 - Fx2);
        Fy4 = Fy3 - (Fy2 - Fy1);
    }else{
        Fx1 = p1x;
        Fy1 = p1y;
 
        Fx2 = p2x;
        Fy2 = p2y;
 
        Fx3 = p3x;
        Fy3 = p3y;
 
        Fx4 = p4x;
        Fy4 = p4y;
    }
 
    floatx1 = Fx1 + worldOriginX;
    floaty1 = Fy1 + worldOriginY;
    floatx2 = Fx2 + worldOriginX;
    floaty2 = Fy2 + worldOriginY;
 
    floatscaleX2 = ((Fx2-Fx1) / scale);
    floatscaleY2 = ((Fy2-Fy1) / scale);
    floatscaleX3 = ((Fx3-Fx4) / scale);
    floatscaleY3 = ((Fy3-Fy4) / scale);
    floatscaleAlpha = (colorT.a - (colorT.a*alpha)) / scale;
 
    floatx3 = x1;
    floaty3 = y1;
    floatx4 = x2;
    floaty4 = y2;
 
    finalfloat u = region.getU();
    finalfloat v = region.getV();
    finalfloat u2 = region.getU2();
    finalfloat v2 = region.getV2();
 
    for(inti = 1; i <= scale; i++) {
        x1 = Fx1 + scaleX2*(i-1) + worldOriginX;
        y1 = Fy1 + scaleY2*(i-1) + worldOriginY;
        x2 = Fx1 + scaleX2*i + worldOriginX;
        y2 = Fy1 + scaleY2*i + worldOriginY;
 
        x3 = Fx4 + scaleX3*i + worldOriginX;
        y3 = Fy4 + scaleY3*i + worldOriginY;
        x4 = Fx4 + scaleX3*(i-1) + worldOriginX;
        y4 = Fy4 + scaleY3*(i-1) + worldOriginY;
 
        color = colorT.toFloatBits();
        colorT.a-=scaleAlpha;
        colorE = colorT.toFloatBits();
 
        vertices[idx++] = x1;
        vertices[idx++] = y1;
        vertices[idx++] = color;
        vertices[idx++] = u;
        vertices[idx++] = v;
 
        vertices[idx++] = x2;
        vertices[idx++] = y2;
        vertices[idx++] = colorE;
        vertices[idx++] = u;
        vertices[idx++] = v2;
 
        vertices[idx++] = x3;
        vertices[idx++] = y3;
        vertices[idx++] = colorE;
        vertices[idx++] = u2;
        vertices[idx++] = v2;
 
        vertices[idx++] = x4;
        vertices[idx++] = y4;
        vertices[idx++] = color;
        vertices[idx++] = u2;
        vertices[idx++] = v;
 
    }
 
    returnvertices;
}

然后将这个网格和纹理一起提供给OpenGL或LibGDX。

就是这样。有乐趣的编码!

参考文献:

  1. Cliffski的博客:绘制激光束的复杂性
    给我一个基本的概述,如何做激光,而不深入到任何细节
  2. 无尽的空间战斗
    灵感!
  3. Ikuraga 
    灵感!
原文出处:http://codepoke.net/2011/12/27/opengl-libgdx-laser-fx/
原创粉丝点击