分页: 1 / 2

[OpenGL+FreeType]模拟黑客帝国数码雨

发表于 : 2016年12月04日 17:16
523066680
还在完善中

初版 Demo

[image 300, 300]http://imgout.ph.126.net/53100071/Matrix01.jpg[/image]

此版通过 freetype 提取汉字轮廓,线绘。

第三阶段完成后开始发代码 (代码有点乱啊)

Re: [OpenGL+FreeType]模拟黑客帝国数码雨

发表于 : 2016年12月04日 17:59
523066680
第二阶段
图片

随机找一些代码
图片

搞明白了二次 Bezier、三次 Bezier 曲线,自己写函数计算轮廓细节。
其次使用现成的 tessellation 函数对轮廓进行三角形细分、填充。

完整程序,以及编译运行所需的库,目标程序在 XP32位 以及 WIN764位平台 测试无误
Demo32_64.7z
(1.66 MiB) 已下载 195 次

Re: [OpenGL+FreeType]模拟黑客帝国数码雨

发表于 : 2016年12月05日 11:07
523066680
在第三阶段出来之前,对前面的效果又做了改善

图片
// Matrix.c, v2.5
// CodeBy: 523066680
// 2016-11

#include <GL/glut.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>

#include <ft2build.h>
#include <freetype/ftbbox.h>
#include <freetype/ftoutln.h>
#include FT_FREETYPE_H

#define SIZE_X 500
#define SIZE_Y 500
#define N_RAIN 50
#define N_TEXT 30

int winID;
FT_Library library;
FT_Face face;

FT_GlyphSlot slot;
FT_Error error;
FT_Outline outline;
FT_BBox box;

GLUtesselator *tobj;

static double vtx[5000][3];
static int vtx_contours[100];
static int Vi;
static int vtx_ctsi;
static float red = 0.0;
static int PARTS = 3.0; //曲线细分程度
static long code;

typedef struct
{
float x;
float y;
}
point;

void GetDatafromOutline(void);
int LoadGlyph(long symbol);

float angx = 0.0;
float angy = 0.0;
float angz = 0.0;
float progress[N_RAIN];
float speed[N_RAIN];
float bright[N_RAIN];
float RXlist[N_RAIN];
float RYlist[N_RAIN];
float RZlist[N_RAIN];
//char uni[] = " abcdefghijklmnopqrstuvwxyz,ABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%&*+-={}[];'<>,.?";

long uni[] = {
32, 30721, 26080, 30721, 20197, 21450, 20154, 20043, 30721, 65292,
22825, 20043, 36947, 65292, 25439, 26377, 20313, 32780, 34917, 19981,
36275, 65292, 26159, 25925, 34394, 32988, 23454, 65292, 19981, 36275,
32988, 26377, 20313, 12290, 20854, 24847, 21338, 65292, 20854, 29702,
22885, 65292, 20854, 36259, 28145, 65292, 22825, 22320, 20043, 35937,
20998, 65292, 38452, 38451, 20043, 20505, 21015, 65292, 21464, 21270,
20043, 30001, 34920, 65292, 27515, 29983, 20043, 20806, 24432, 65292,
19981, 35851, 32780, 36951, 36857, 33258, 21516, 65292, 21247, 32422,
32780, 24189, 26126, 26031, 22865, 65292, 31293, 20854, 35328, 26377,
24494, 65292, 39564, 20043, 20107, 19981, 24530, 65292, 35802, 21487,
35859, 33267, 36947, 20043, 23447, 65292, 22857, 29983, 20043, 22987,
30691, 12290, 20551, 33509, 22825, 26426, 36805, 21457, 65292, 22937,
35782, 29572, 36890, 65292, 25104, 35851, 34429, 23646, 20046, 29983,
30693, 65292, 26631, 26684, 20134, 36164, 20110, 27835, 35757, 65292,
26410, 23581, 26377, 34892, 19981, 30001, 36865, 65292, 20986, 19981,
30001, 20135, 32773, 20134, 12290, 28982, 21051, 24847, 30740, 31934,
65292, 25506, 24494, 32034, 38544, 65292, 25110, 35782, 22865, 30495,
35201, 65292, 21017, 30446, 29275, 26080, 20840, 65292, 25925, 21160,
21017, 26377, 25104, 65292, 29369, 39740, 31070, 24189, 36190, 65292,
32780, 21629, 19990, 22855, 26480, 65292, 26102, 26102, 38388, 20986,
28937, 12290, 86, 73, 67, 26472
};
static int n_char;
static int arr[N_RAIN][N_TEXT];
static float alpha[N_RAIN][N_TEXT];

void beginCallback(GLenum which)
{
glBegin(which);
}

void endCallback(void)
{
glEnd();
}

void errorCallback(GLenum errorCode)
{
const GLubyte *estring;

estring = gluErrorString(errorCode);
fprintf(stderr, "Tessellation Error: %s\n", estring);
exit(0);
}

void vertexCallback(GLdouble *vertex)
{
//const GLdouble *pointer;
//pointer = (GLdouble *) vertex;
glNormal3f(0.0, 0.0, 1.0);
glVertex3dv(vertex);
}

void CALLBACK combineCallback(GLdouble coords[3], GLdouble *data[4],
GLfloat weight[4], GLdouble **dataOut )
{
GLdouble *vertex;
vertex = (GLdouble *) malloc(3 * sizeof(GLdouble));

vertex[0] = coords[0];
vertex[1] = coords[1];
vertex[2] = coords[2];
*dataOut = vertex;
}

void PointOnQuadBeizer(point cp[3], double t, double *vt )
{
double cx, bx, ax, cy, by, ay;

ax = cp[1].x - cp[0].x;
ay = cp[1].y - cp[0].y;

bx = cp[2].x - cp[1].x;
by = cp[2].y - cp[1].y;

cx = (bx * t + cp[1].x) - (ax * t + cp[0].x);
cy = (by * t + cp[1].y) - (ay * t + cp[0].y);

vt[0] = cx*t + (ax * t + cp[0].x);
vt[1] = cy*t + (ay * t + cp[0].y);
vt[2] = 0.0;
}

void display(void)
{
int errcode;
int bgn;
int i, j;
int start;
// 清理颜色缓冲区
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glPushMatrix();
glRotatef(angy, 0.0, 0.5, 0.0);
glRotatef(angx, 1.0, 0.0, 0.0);
glRotatef(angz, 0.0, 0.0, 1.0);

glColor3f(0.3, 0.8, 0.3);

for (int m = 0; m < N_RAIN; m++)
{
glPushMatrix();

//glTranslatef(-12000.0 + RXlist[m], 5000.0 + RYlist[m], RZlist[m]);
glTranslatef(RXlist[m], RYlist[m]+6000.0, RZlist[m]);
glRotatef( (float)m/(float)N_RAIN * 60.0 , 0.0, 1.0, 0.0 );
for (int n = 0; n < N_TEXT ; n++ )
{
//向下移位
glTranslatef(0.0, -1000.0, 0.0);

//Matrix for scale
glPushMatrix();
// glRotatef(90.0, 0.0, 0.0, 1.0);
if ( n == bright[m] )
{
glScalef(1.1, 1.1, 1.1);
glColor4f(0.2, 0.8, 0.2, 1.0);
}
else
{
glColor4f(0.1, (float)n/(float)N_TEXT+0.5, 0.5, alpha[m][n] );
}

alpha[m][n] -= 0.03;
glCallList( arr[m][n] );
glPopMatrix();
}
glPopMatrix();

}
glPopMatrix();

// glPushMatrix();
// glTranslatef(-15000.0, 15000.0, 0);
// glColor3f(0.3, 0.7, 0.5);
// for (i = 3; i >=0; i--)
// {
// glCallList( n_char - i );
// glTranslatef(1000.0, 0.0, 0.0);
// }
// glPopMatrix();

glutSwapBuffers();
}

void idle(void)
{
usleep(30000);
angy += 0.3;
//angz += 1.0;

int r;
for (int i = 0; i < N_RAIN; i++ )
{
if ( progress[i] < n_char )
{
progress[i] += speed[i];
arr[i][ (int)progress[i] % N_TEXT ] = (int)progress[i];
alpha[i][ (int)progress[i] % N_TEXT ] = 1.0;
}
else
{
progress[i] = 1.0;
RYlist[i] = (float)(rand() % 20 - 10) * 1000.0;
}
bright[i] = (int)progress[i] % N_TEXT;
}


glutPostRedisplay();
}

void reshape(int Width, int Height)
{
const float fa = 20000.0;
const float half = 8000.0;

float w = (float)Width;
float h = (float)Height;

glViewport(0, 0, Width, Height); //视口范围
glMatrixMode(GL_PROJECTION); // 投影视图矩阵
glLoadIdentity();
//glOrtho(-half, half, -half, half, 0.0, 40000.0);
gluPerspective(80.0, w/h, 100.0, 40000.0);
glMatrixMode(GL_MODELVIEW); // 模型视图矩阵
glLoadIdentity();
gluLookAt(0.0,0.0,fa, 0.0,0.0,0.0, 0.0,1.0,fa);
// 观察点, 朝向的坐标, 观察点向上坐标
}

void keypress(unsigned char key, int mousex, int mousey)
{
int errcode;
switch (key)
{
case 'q':
case 'Q':
glutDestroyWindow(winID);
exit(0);
break;
case 'w':
case 'W':
angx += 1.0;
glutPostRedisplay();
break;
case 's':
case 'S':
angx -= 1.0;
glutPostRedisplay();
break;
case 'a':
case 'A':
angz += 1.0;
glutPostRedisplay();
break;
case 'd':
case 'D':
angz -= 1.0;
glutPostRedisplay();
break;
case 'j':
case 'J':
angy += 2.0;
glutPostRedisplay();
break;
case 'k':
case 'K':
angy -= 2.0;
glutPostRedisplay();
break;
}
}

void init(void)
{
int errcode;
Vi = 0;
vtx_ctsi = 0;
int bgn;
int next;
int base; //for Display List

//glutFullScreen();
srand( time(NULL) );
glClearColor(0.0, 0.0, 0.0, 0.0);
glLineWidth( 1.0 );
glPointSize( 2.0 );
glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
//glEnable(GL_POLYGON_SMOOTH);

tobj = gluNewTess();
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
gluTessCallback(tobj, GLU_TESS_VERTEX, glVertex3dv);
gluTessCallback(tobj, GLU_TESS_BEGIN, beginCallback);
gluTessCallback(tobj, GLU_TESS_END, endCallback);
gluTessCallback(tobj, GLU_TESS_ERROR, errorCallback);
gluTessCallback(tobj, GLU_TESS_COMBINE,combineCallback);
//gluTessProperty(tobj, GLU_TESS_BOUNDARY_ONLY, GL_TRUE);
gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);

for (int i = 0; i < N_RAIN; i++)
{
//制造参差效果
RXlist[i] = (float)(rand() % 20 - 10) * 1500.0;
RYlist[i] = (float)(rand() % 20 - 10) * 500.0;
RZlist[i] = (float)(rand() % 20 - 10) * 1000.0;

//随机进度、递进速度
progress[i] = (float) ((rand()%50 + 1) * 1);
speed[i] = (float) ( rand()%5 + 2) / 10.0;

//填入空白;显示列表的索引,从1开始
for (int j = 0; j < N_TEXT; j++)
{
arr[i][j] = 1;
alpha[i][j] = 1.0;
}
}

// 创建显示列表
n_char = sizeof(uni)/sizeof(uni[0]);
base = glGenLists( n_char );

//每个字符的点数据(已经对曲线求值了的)整理到 glyph 数组
for (int code = 0; code < n_char; code++ )
{
errcode = LoadGlyph( uni[code] );
//处理坐标、曲线数据到vtx数组
GetDatafromOutline();

glNewList(base+code, GL_COMPILE);
gluTessBeginPolygon(tobj, NULL);
for (int cti = 0; cti < vtx_ctsi ; cti++ )
{
gluTessBeginContour(tobj);
int pti = (cti == 0 ? 0 : vtx_contours[cti-1] + 1 );
for (; pti <= vtx_contours[cti]; pti++ )
{
gluTessVertex(tobj, vtx[pti], vtx[pti] );
}
gluTessEndContour(tobj);
}
gluTessEndPolygon(tobj);

for (int cti = 0; cti < vtx_ctsi; cti++ )
{
glBegin(GL_QUAD_STRIP);
int pti = (cti == 0 ? 0 : vtx_contours[cti-1] + 1 );
for (; pti <= vtx_contours[cti]; pti++ )
{
glVertex3f( vtx[pti][0], vtx[pti][1], 0.0 );
glVertex3f( vtx[pti][0], vtx[pti][1], -20.0 );
}
glEnd();
}

glEndList();
}

}

void ftinit(void)
{
char* filename;
filename = "C:/windows/fonts/STXingKa.ttf";

error = FT_Init_FreeType( &library );
error = FT_New_Face( library, filename, 0, &face ); /* create face object */
}

int LoadGlyph(long symbol)
{
//这里可以是unicode编码值,字体必须支持才行
FT_UInt index = FT_Get_Char_Index(face, symbol);

if (index <= 0)
{
printf("Error in %d\n", symbol);
return 1;
}
else
{
//printf("no problem in %d\n", symbol);
FT_Error error = FT_Load_Glyph(face,
index,
FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
slot = face->glyph;
outline = slot->outline;
FT_Outline_Get_BBox( &outline, &box );
return 0;
}
}

//Function by 523066680
void GetDatafromOutline(void)
{
Vi = 0;
vtx_ctsi = 0;
int bgn;
int next;
point lastpt;
point cp[3];

for (int cts = 0; cts < outline.n_contours; cts++ )
{
bgn = ( cts == 0 ? 0 : outline.contours[cts-1] + 1 );

for (int i = bgn; i <= outline.contours[cts] ; i++)
{
//i==终点时,next回到某个轮廓的起点
next = ( i == outline.contours[cts] ? bgn : i+1 );

if ( outline.tags[i] == 0 )
{
cp[1].x = (double)outline.points[i].x;
cp[1].y = (double)outline.points[i].y;

if ( outline.tags[i-1] == 1 ) //1, 0
{
cp[0].x = (double)outline.points[i-1].x;
cp[0].y = (double)outline.points[i-1].y;

if (outline.tags[next] == 1) //1, 0, 1
{
cp[2].x = (double) outline.points[next].x;
cp[2].y = (double) outline.points[next].y;

for (float i = 0.0; i < PARTS; i+=1.0 )
{
PointOnQuadBeizer(cp, i/PARTS, vtx[Vi]);
Vi++;
}
}
else //1, 0, 0
{
cp[2].x = (double)(outline.points[i].x + outline.points[next].x)/2;
cp[2].y = (double)(outline.points[i].y + outline.points[next].y)/2;

for (float i=0.0; i <PARTS; i+=1.0 )
{
PointOnQuadBeizer(cp, i/PARTS, vtx[Vi]);
Vi++;
}
}
}
else //0, 0
{
//起点为上一段曲线的终点 而上一段曲线终点可能是计算出来的
cp[0].x = lastpt.x;
cp[0].y = lastpt.y;

if ( outline.tags[next] == 0 ) //0, 0, 0
{
cp[2].x = (double)(outline.points[i].x + outline.points[next].x)/2 ;
cp[2].y = (double)(outline.points[i].y + outline.points[next].y)/2 ;

for (float i=0.0; i < PARTS; i+=1.0 )
{
PointOnQuadBeizer(cp, i/PARTS, vtx[Vi]);
Vi++;
}
}
else //0, 0, 1
{
cp[2].x = (double)outline.points[next].x ;
cp[2].y = (double)outline.points[next].y ;

for (float i=0.0; i < PARTS; i+=1.0 )
{
PointOnQuadBeizer(cp, i/PARTS, vtx[Vi]);
Vi++;
}
}
}
lastpt.x = cp[2].x;
lastpt.y = cp[2].y;
}
else
{
//直线线段 只记录当前点
if ( outline.tags[next] == 1 ) //1, 1
{
vtx[Vi][0] = outline.points[i].x;
vtx[Vi][1] = outline.points[i].y;
vtx[Vi][2] = 0.0;
Vi++;
}
}
}

vtx_contours[vtx_ctsi] = Vi-1;
vtx_ctsi++;
}
//printf("vtx_ctsi: %d, nvtx: %d\n", vtx_ctsi, Vi);
}

int main( int argc, char** argv )
{

ftinit();
LoadGlyph(code);
GetDatafromOutline();

printf("font-name: %s, style: %s\n", face->family_name, face->style_name);
printf("n_faces:%d, face_index:%d, face_flags:%d, style_flags:%d\n",
face->num_faces,
face->face_index,
face->face_flags,
face->style_flags
);

printf("numglyphs :%d, fixed_sizes: %d, generic: %d\n",
face->num_glyphs,
face->num_fixed_sizes,
face->generic
);

printf("bbox x: %d - %d, y: %d - %d, ascender %d, descender %d, height %d \n",
face->bbox.xMin,face->bbox.xMax,face->bbox.yMin,face->bbox.yMax,
face->ascender,
face->descender,
face->height
);

printf("n_contours: %d \n n_points: %d \n flags: %d\n",
outline.n_contours,
outline.n_points,
outline.flags
);

for (int i=0; i<outline.n_contours; i++ )
{
printf("Contour %d: %d\n", i, outline.contours[i]);
}
printf("\n");

// for (int i = 0; i<outline.n_points ; i++)
// {
// printf("%2d : %4d %4d %d\n",
// i,
// outline.points[i].x,
// outline.points[i].y,
// outline.tags[i]
// );
// }

glutInit(&argc, argv);
//显示模式 双缓冲 RGBA
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA |GLUT_DEPTH | GLUT_MULTISAMPLE );
glutInitWindowSize(SIZE_X, SIZE_Y); //窗口大小
glutInitWindowPosition(200, 200); //位置
winID = glutCreateWindow("ViANG_5230"); //窗口句柄
init();
glutDisplayFunc(display); //显示
glutKeyboardFunc(keypress); //按键事件响应
glutReshapeFunc(reshape); //窗口事件响应
glutIdleFunc(idle); //闲时回调函数
glutMainLoop(); //开始主循环

FT_Done_Face ( face );
FT_Done_FreeType( library );

return 0;
}
:Compile gcc -std=c11 "%1" -o "%~n1" ^ -ID:\Lib\freeglut-MinGW-3.0.0-1.mp\include -ID:/lib/freetype-2.7/include ^ -LD:\Lib\freeglut-MinGW-3.0.0-1.mp\lib\x64 -LD:/lib/freetype-2.7/objs/.libs ^ -lfreeglut -lopengl32 -lglu32 -lfreetype -lpng -lz

Re: [OpenGL+FreeType]模拟黑客帝国数码雨

发表于 : 2016年12月06日 00:27
523066680
V3.0 开发中
mtx-part3.png

Re: [OpenGL+FreeType]模拟黑客帝国数码雨

发表于 : 2016年12月06日 10:08
happy886rr
牛啊,这个怎么玩,需要下载freetype吗?

Re: [OpenGL+FreeType]模拟黑客帝国数码雨

发表于 : 2016年12月06日 10:20
523066680
happy886rr 写了:牛啊,这个怎么玩,需要下载freetype吗?
是的需要 freetype 库的支持,相关的帖子:
[Freetype]MinGW编译 FreeType2.4 以及 示例代码
[FreeType]获取字符点阵的最小示例代码
[FreeType]提取、绘制字体的字形轮廓

MinGW + FreeGLUT 环境配置(glfw glew 可以暂时不管)
MinGW+OpenGL+freeglut+glew+glfw 环境配置

为了写这个效果也着实是做了不少准备工作,Bezier曲线、Freetype提取文字轮廓,OpenGL的多边形细分+填充,都是现学现用。

Re: [OpenGL+FreeType]模拟黑客帝国数码雨

发表于 : 2016年12月06日 10:24
happy886rr
好的,我也需要下载个最新的mingw了。我一直用的精简版mingw各种头文件缺失。

Re: [OpenGL+FreeType]模拟黑客帝国数码雨

发表于 : 2016年12月06日 16:40
echo
哈哈,我不喜欢用所谓各种精简软件的原因。 :crazylaugh2
碰到意外问题还在浪费时间来处理。

Re: [OpenGL+FreeType]模拟黑客帝国数码雨

发表于 : 2016年12月06日 17:49
happy886rr
523066680 写了: 是的需要 freetype 库的支持,相关的帖子:
[Freetype]MinGW编译 FreeType2.4 以及 示例代码
freetype2.7都编译好了,exe也生成了,但是一运行就崩溃,断点发现在 glutDisplayFunc(display); //显示出了问题,是不是还需要和字符文件放一起,需要libpng.lib的库是哪个版本。我现在估计问题就在png或者是字库。

Re: [OpenGL+FreeType]模拟黑客帝国数码雨

发表于 : 2016年12月06日 17:56
523066680
happy886rr 写了:
523066680 写了: 是的需要 freetype 库的支持,相关的帖子:
[Freetype]MinGW编译 FreeType2.4 以及 示例代码
freetype2.7都编译好了,exe也生成了,但是一运行就崩溃,断点发现在 glutDisplayFunc(display); //显示出了问题,是不是还需要和字符文件放一起,需要libpng.lib的库是哪个版本。我现在估计问题就在png或者是字库。
我也遇到过,一般是缺少 freeglut.dll,或者 dll 的32位、64位版本不对