我正在用Java开发Android应用程序。我想绘制动态图像,如附加文件(非常旧的DOS程序的打印屏幕)。我认为这是水波。
任何人都能解释我怎么能做这份工作?我不知道这些照片是如何绘制的。
谢谢!
P.S。可能是可压缩流体中的行波?
已编辑:包含所需动画的屏幕记录:http://www.youtube.com/watch?v=_zeSQX_8grY
EDITED2:我找到了此视频效果的来源here。有一个DOS编译程序(可以在DOS框中运行)和ASM中的课程。文件夹“PART3”包含所需视频效果的来源(文件WPLASMA.ASM)。不幸的是我不知道Turbo Assembler。有人可以帮我理解这个节目如何吸引这种视频效果吗?我发布了WPLASMA.ASM here的内容。
EDITED3:我已将代码的大部分内容移植到C.但我不知道VGA模式是如何工作的。我对PutBmp功能有困难。
#include <cmath>
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <opencv2/highgui/highgui.hpp>
struct RGB {
char red, green, blue;
};
#define MAXH 60 // horiz wave length.
#define MAXVW 64 // vert wave length.
#define MAXHW 32 // max horiz wave amount.
#define MAXV (80 + MAXHW) // vert wave length.
static void UpdHWaves( char* HWave1, char* HWave2,
int& HWavPos1, int& HWavPos2,
int HWavInc1 ) // Updates the Horiz Waves.
{
for( int i = 0; i < MAXH - 1; ++i ) {
HWave1[ i ] = HWave1[ i + 1 ];
}
int8_t val = 127 * std::sin( HWavPos1 * M_PI / 180.0 );
HWave1[ MAXH - 1 ] = val >> 1;
HWavPos1 += HWavInc1;
if( HWavPos1 >= 360 ) {
HWavPos1 -= 360;
}
for( int i = 0; i < MAXH; ++i ) {
val = 127 * std::sin( ( HWavPos2 + i * 4 ) * M_PI / 180.0 );
val = ( val >> 1 ) + HWave1[ i ];
HWave2[ i ] = ( val >> 3 ) + 16;
}
HWavPos2 += 4;
if( HWavPos2 >= 360 ) {
HWavPos2 -= 360;
}
}
static void UpdVWaves( char *VWave1, char* VWave2,
int& VWavPos1, int& VWavPos2,
int VWavInc1 )
{
for( int i = 0; i < MAXV - 1; ++i ) {
VWave1[ i ] = VWave1[ i + 1 ];
}
int8_t val = 127 * std::sin( VWavPos1 * M_PI / 180.0 );
VWave1[ MAXV - 1 ] = val >> 1;
VWavPos1 += VWavInc1;
if( VWavPos1 >= 360 ) {
VWavPos1 -= 360;
}
for( int i = 0; i < MAXV; ++i ) {
val = 127 * std::sin( ( VWavPos2 + i * 3 ) * M_PI / 180.0 );
val = ( val >> 1 ) + VWave1[ i ];
VWave2[ i ] = ( val >> 2 ) + 32;
}
++VWavPos2;
if( VWavPos2 >= 360 ) {
VWavPos2 -= 360;
}
}
static void UpdBmp( char *Bitmap, const char *VWave2 ) // Updates the Plasma bitmap.
{
for( int k = 0; k < MAXV; ++k ) {
char al = VWave2[ k ];
int i = 0;
for( int l = 0; l < MAXH; ++l ) {
++al;
Bitmap[ i ] = al;
i += 256;
}
++Bitmap;
}
}
static void PutBmp( const RGB* palete,
const char* BitMap,
const char* HWave2 ) // Puts into the screen the Plasma bitmap.
{
RGB screen[320*200];
memset( screen, 0, sizeof( screen ) );
RGB *screenPtr = screen;
const char *dx = BitMap;
const char *si = HWave2;
for( int i = 0; i < MAXH; ++i ) {
char ax = *si;
++si;
const char *si2 = ax + dx;
for( int j = 0; j < 40; ++j ) {
assert( *si2 < MAXH + MAXVW );
*screenPtr = palete[ *si2 ];
++screenPtr;
++si2;
assert( *si2 < MAXH + MAXVW );
*screenPtr = palete[ *si2 ];
++screenPtr;
++si2;
}
dx += 256;
}
static cv::VideoWriter writer( "test.avi", CV_FOURCC('M','J','P','G'), 15, cv::Size( 320, 200 ) );
cv::Mat image( 200, 320, CV_8UC3 );
for( int i = 0; i < 200; ++i ) {
for( int j = 0; j < 320; ++j ) {
image.at<cv::Vec3b>(i, j )[0] = screen[ 320 * i + j ].blue;
image.at<cv::Vec3b>(i, j )[1] = screen[ 320 * i + j ].green;
image.at<cv::Vec3b>(i, j )[2] = screen[ 320 * i + j ].red;
}
}
writer.write( image );
}
int main( )
{
RGB palete[256];
// generation of the plasma palette.
palete[ 0 ].red = 0;
palete[ 0 ].green = 0;
palete[ 0 ].blue = 0;
RGB *ptr = palete + 1;
int ah = 0;
int bl = 2;
for( int i = 0; i < MAXH + MAXVW; ++i ) {
ptr->red = 32 - ( ah >> 1 );
ptr->green = 16 - ( ah >> 2 );
ptr->blue = 63 - ( ah >> 2 );
ah += bl;
if( ah >= 64 ) {
bl = - bl;
ah += 2 * bl;
}
ptr += 1;
}
//setup wave parameters.
int HWavPos1 = 0; // horiz waves pos.
int HWavPos2 = 0;
int VWavPos1 = 0; // vert waves pos.
int VWavPos2 = 0;
int HWavInc1 = 1; // horiz wave speed.
int VWavInc1 = 7; // vert wave speed.
char HWave1[ MAXH ]; // horiz waves.
char HWave2[ MAXH ];
char VWave1[ MAXV ]; // vert waves.
char VWave2[ MAXV ];
char Bitmap[ 256 * MAXH + MAXV ];
memset( Bitmap, 0, sizeof( Bitmap ) );
//use enough steps to update all the waves entries.
for( int i = 0; i < MAXV; ++i ) {
UpdHWaves( HWave1, HWave2, HWavPos1, HWavPos2, HWavInc1 );
UpdVWaves( VWave1, VWave2, VWavPos1, VWavPos2, VWavInc1 );
}
std::srand(std::time(0));
for( int i = 0; i < 200; ++i ) {
UpdHWaves( HWave1, HWave2, HWavPos1, HWavPos2, HWavInc1 );
UpdVWaves( VWave1, VWave2, VWavPos1, VWavPos2, VWavInc1 );
UpdBmp( Bitmap, VWave2 );
PutBmp( palete, Bitmap, HWave2 );
//change wave's speed.
HWavInc1 = ( std::rand( ) & 7 ) + 3;
VWavInc1 = ( std::rand( ) & 3 ) + 5;
}
return 0;
}
答案 0 :(得分:2)
你能说出DOS程序吗?或者在YouTube上找到类似的效果?
猜测,它是一种“等离子”效应,曾经在演示场景中很常见。您可以在Tempest 2000的PC版本菜单的背景中看到一个,包括this YouTube video中的非常短暂的内容。那看起来不错吗?
如果是这样,那么就像所有演示效果一样,它是烟雾和镜子。要在OpenGL中重新创建一个,您需要生成具有球形正弦图案的纹理。因此,对于每个像素,计算出距离中心的距离。得到那个距离的正弦乘以你认为美学上令人愉悦的数字。将该值存储到纹理中。确保缩放以填充整个字节。你应该得到一个看起来像池塘表面涟漪的图像。
要产生最终输出,您将至少合并其中三个。如果你要做三个然后将每个乘以1/3,那么帧缓冲中的值最终会在0-255范围内。你将独立地移动这三个东西以产生动画,也可以通过正弦函数 - 例如一个人可能会遵循路径centre + (0.3 * sin(1.8 + time * 1.5), 0.8 * sin(0.2 + time * 9.2))
,其他人也会遵循该形式的功能。根据需要调整时间倍数,角度偏移和轴倍增器。
还有一个正弦模式可以应用:如果这是一个DOS程序,你还需要设置你的调色板,使亮度以正弦波形式出现 - 例如颜色0-31将是一个完整的循环,32-63将是循环的重复,等等。您无法在现代设备上设置调色板,OpenGL ES不会执行调色板纹理,因此您将不得不写一个着色器。从好的方面来说,三角函数是内置于GLSL中的,因此它将是一个相当简单的函数。
编辑:我把一个快速测试项目汇总在一起并编写了以下顶点着色器:attribute vec4 position;
attribute vec2 texCoord;
uniform mediump float time;
varying highp vec2 texCoordVarying1, texCoordVarying2, texCoordVarying3;
void main()
{
mediump float radiansTime = time * 3.141592654 * 2.0;
/*
So, coordinates here are of the form:
texCoord + vec2(something, variant of same thing)
Where something is:
<linear offset> + sin(<angular offset> + radiansTime * <multiplier>)
What we're looking to do is to act as though moving three separate sheets across
the surface. Each has its own texCoordVarying. Each moves according to a
sinusoidal pattern. Note that the multiplier is always a whole number so
that all patterns repeat properly as time goes from 0 to 1 and then back to 0,
hence radiansTime goes from 0 to 2pi and then back to 0.
The various constants aren't sourced from anything. Just play around with them.
*/
texCoordVarying1 = texCoord + vec2(0.0 + sin(0.0 + radiansTime * 1.0) * 0.2, 0.0 + sin(1.9 + radiansTime * 8.0) * 0.4);
texCoordVarying2 = texCoord - vec2(0.2 - sin(0.8 + radiansTime * 2.0) * 0.2, 0.6 - sin(1.3 + radiansTime * 3.0) * 0.8);
texCoordVarying3 = texCoord + vec2(0.4 + sin(0.7 + radiansTime * 5.0) * 0.2, 0.5 + sin(0.2 + radiansTime * 9.0) * 0.1);
gl_Position = position;
}
...和片段着色器:
varying highp vec2 texCoordVarying1, texCoordVarying2, texCoordVarying3;
void main()
{
/*
Each sheet is coloured individually to look like ripples on
the surface of a pond after a stone has been thrown in. So it's
a sine function on distance from the centre. We adjust the ripple
size with a quick multiplier.
Rule of thumb: bigger multiplier = smaller details on screen.
*/
mediump vec3 distances =
vec3(
sin(length(texCoordVarying1) * 18.0),
sin(length(texCoordVarying2) * 14.2),
sin(length(texCoordVarying3) * 11.9)
);
/*
We work out outputColour in the range 0.0 to 1.0 by adding them,
and using the sine of that.
*/
mediump float outputColour = 0.5 + sin(dot(distances, vec3(1.0, 1.0, 1.0)))*0.5;
/*
Finally the fragment colour is created by linearly interpolating
in the range of the selected start and end colours 48 36 208
*/
gl_FragColor =
mix( vec4(0.37, 0.5, 1.0, 1.0), vec4(0.17, 0.1, 0.8, 1.0), outputColour);
}
/*
Implementation notes:
it'd be smarter to adjust the two vectors passed to mix so as not
to have to scale the outputColour, leaving it in the range -1.0 to 1.0
but this way makes it clearer overall what's going on with the colours
*/
将它放入一个绘制四边形的项目中,以显示两个维度中的texCoord范围[0,1](宽高比被诅咒)和设置时间,使其每分钟从0到1运行一次,这给了我: / p>
它显然不完全相同,但效果相同。你需要调整各种魔法常数,直到得到你满意的东西。
编辑2:它不会帮助你那么多,但我把这个GL ES代码放到合适的iOS包装器中uploaded to GitHub。
答案 1 :(得分:1)
Android NDK中有一个名为“bitmap-plasma”的示例程序,它会产生非常相似的模式。它在C中,但可能会转换为GLSL代码。