注册 登录
主题 : 代码片段:用shader实现的生命游戏
级别: 新手上路
UID: 27736
精华: 0
发帖: 59
可可豆: 168 CB
威望: 108 点
在线时间: 76(时)
注册时间: 2010-08-16
最后登录: 2017-06-27
0 楼:  发表于: 2014-01-02 18:15    发自: Web Page

代码片段:用shader实现的生命游戏   

管理提醒: 本帖被 angellixf 执行加亮操作(2014-01-02)
这个程序的功能就是鼎鼎大名的“生命游戏”(不知道的戳这里:http://zh.wikipedia.org/wiki/%E7%94%9F%E5%91%BD%E6%B8%B8%E6%88%8F )。在不用HashLife算法的情况下(想了解这个反直觉地高效的算法有多逆天可去AppStore下载“golly”来玩玩),我这个恐怕是最快的了,因为是直接用GPU去计算嘛。
运行效果见文末截图(其中有一点奇怪是不知道为何“世界”并没覆盖整个屏幕而只是一个局部,在其边缘则是无穷发散的。不过这样也挺好,生命游戏要进化得好就必须不限定边界,才能体现出一些奇特的生长性质):

首先在Xcode中创建一个cocos2dx工程,在HelloWorldScene.h中往HelloWorld类声明区添加如下成员,并重写draw()方法:

    virtual void draw();
    
private:
    
    CCGLProgram* _golShader;
    
    CCRenderTexture* _renderTextures[2];
    
    int    _currentBuffer;



再将HelloWorld::init()里的原有代码替换为如下内容:

if ( !CCLayer::init() )
    {
        return false;
    }

    CCSize size = CCDirector::sharedDirector()->getWinSize();
    int pow2Width = ccNextPOT(size.width * CC_CONTENT_SCALE_FACTOR());
    int pow2Height = ccNextPOT(size.height * CC_CONTENT_SCALE_FACTOR());
    (_renderTextures[0] = CCRenderTexture::create(pow2Width, pow2Height))->retain();
    (_renderTextures[1] = CCRenderTexture::create(pow2Width, pow2Height))->retain();
    
    ccDrawColor4F(0, 1, 0, 1);
    _renderTextures[_currentBuffer]->beginWithClear(0, 0, 0, 0);
    for (int x=size.width-1; x>=0; x--)
        for (int y=size.height-1; y>=0; y--)
        {
            if ((float)random() / (float)INT32_MAX <= 0.21828)
            {
                ccDrawPoint(ccp(x,y));
            }
        }
    _renderTextures[_currentBuffer]->end();
    
    (_golShader = new CCGLProgram)->initWithVertexShaderByteArray(ccPositionTexture_vert, (GLchar*) CCString::createWithContentsOfFile(CCFileUtils::sharedFileUtils()->fullPathForFilename("gol_frag.glsl").c_str())->getCString());
    _golShader->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);
    _golShader->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);
    _golShader->link();
    _golShader->updateUniforms();
    glUniform1f(glGetUniformLocation(_golShader->getProgram(), "dx"), 1.0 / pow2Width);
    glUniform1f(glGetUniformLocation(_golShader->getProgram(), "dy"), 1.0 / pow2Height);
    
    return true;



HelloWorld::draw()方法如下:

void HelloWorld::draw()
{
    _currentBuffer = 1 - _currentBuffer;
    _renderTextures[_currentBuffer]->beginWithClear(0, 0, 0, 0);
    _renderTextures[1 - _currentBuffer]->getSprite()->getTexture()->setShaderProgram(_golShader);
    _renderTextures[1 - _currentBuffer]->getSprite()->getTexture()->drawAtPoint(CCPointZero);
    _renderTextures[_currentBuffer]->end();
    _renderTextures[_currentBuffer]->getSprite()->getTexture()->drawAtPoint(ccp(0,0));
}



再在Resources组里新建一个gol_frag.glsl文件,其内容为:

#ifdef GL_ES
precision highp float;
#endif

varying highp vec2 v_texCoord;

uniform sampler2D CC_Texture0;

uniform highp float dx;
uniform highp float dy;

void main()
{
    vec2 tc = vec2(v_texCoord.x, 1.0 - v_texCoord.y);
    int neighbours = 0;
    for (int ix=-1; ix<2; ix++)
    for (int iy=-1; iy<2; iy++)
    {
        if (texture2D(CC_Texture0, tc + vec2(float(ix)*dx, float(iy)*dy)).rgb != vec3(0,0,0))
            neighbours++;
    }
    if (texture2D(CC_Texture0, tc).rgb != vec3(0,0,0))
        neighbours--;
    
    if (neighbours < 2 || neighbours > 3)
        gl_FragColor = vec4(0,0,0,1);
    else if (neighbours == 3)
        gl_FragColor = vec4(0,1,0,1);
    else
        gl_FragColor = texture2D(CC_Texture0, tc);
}




 下面计算一下总的代码行数:以上在HelloWorld类中添加的代码算上空行也才共计50行,但别忘了init()方法里我是删了向导默认生成的40行代码呀……

所以净添加的代码只有十几行而已……
……
……
……
……
……
……
……
 来打我呀打我呀打我呀……
…… 

还有人可能会说,shader的代码不算吗?

不算,因为那是添加在Resources里的资源文件……

……
……
……
……
……
好吧别打了别打了我错了还不成吗?
[ 此帖被godspeed1024在2014-01-03 22:14重新编辑 ]

图片:IMG_0619.PNG
图片:IMG_0617.PNG
附件: MiniGOL.zip (6575 K) 下载次数:38
级别: 新手上路
UID: 27736
精华: 0
发帖: 59
可可豆: 168 CB
威望: 108 点
在线时间: 76(时)
注册时间: 2010-08-16
最后登录: 2017-06-27
1 楼:  发表于: 2014-01-02 18:17    发自: Web Page
回 楼主(godspeed1024) 的帖子
我要参加40行代码比赛!求人气!
级别: 新手上路
UID: 27736
精华: 0
发帖: 59
可可豆: 168 CB
威望: 108 点
在线时间: 76(时)
注册时间: 2010-08-16
最后登录: 2017-06-27
2 楼:  发表于: 2014-01-02 18:24    发自: Web Page
关于为什么整个“世界”会出现奇怪的“板块漂移”现象,我至今也没找到原因,但是可以肯定生命游戏的算法实现是正确的,因为这是我之前做的一个更完整的生命游戏的简化版,其中还有手势放大和平移功能。
[ 此帖被godspeed1024在2014-01-03 10:41重新编辑 ]

级别: 新手上路
UID: 27736
精华: 0
发帖: 59
可可豆: 168 CB
威望: 108 点
在线时间: 76(时)
注册时间: 2010-08-16
最后登录: 2017-06-27
3 楼:  发表于: 2014-01-02 18:34    发自: Web Page
之前做的带手势缩放查看的版本。当时是直接用OpenGL函数写的,没用cocos2d-x提供的CCRenderTexture类。用了之后代码简化多了。
附件: GameOfLife.zip (6972 K) 下载次数:14
级别: 新手上路
UID: 27736
精华: 0
发帖: 59
可可豆: 168 CB
威望: 108 点
在线时间: 76(时)
注册时间: 2010-08-16
最后登录: 2017-06-27
4 楼:  发表于: 2014-01-02 20:07    发自: Web Page
不知为何,直接用OpenGL函数写的就没有“漂移”问题,而用CCRenderTexture的就有问题。不知是否根源于Cocos2d-X本身的BUG?
级别: *
UID: 90233
精华: *
发帖: *
可可豆: * CB
威望: * 点
在线时间: (时)
注册时间: *
最后登录: *
5 楼:  发表于: 2014-01-02 22:40    发自: Web Page
回 楼主(godspeed1024) 的帖子
顶一个  
级别: 新手上路

状态: 连续签到 - [2天]
UID: 161856
精华: 0
发帖: 20
可可豆: 13 CB
威望: 46 点
在线时间: 22(时)
注册时间: 2012-08-19
最后登录: 2014-08-31
6 楼:  发表于: 2014-01-03 09:03    发自: Web Page
牛气。


象牙绘  最精致的校园助手
级别: 风云使者

状态: 连续签到 - [309天]
UID: 83747
精华: 0
发帖: 3865
可可豆: 19534 CB
威望: 19405 点
在线时间: 2397(时)
注册时间: 2011-07-27
最后登录: 2018-11-05
7 楼:  发表于: 2014-01-03 09:48    发自: Web Page
哎呦,很屌哦。
级别: 风云使者

状态: 连续签到 - [30天]
UID: 183396
精华: 4
发帖: 4881
可可豆: 5742 CB
威望: 5905 点
在线时间: 1808(时)
注册时间: 2012-12-04
最后登录: 2019-02-25
8 楼:  发表于: 2014-01-03 10:05    发自: Web Page
骚年,你这么屌,你父母一定会知道的
级别: 精灵王

状态: 连续签到 - [341天]
UID: 4020
精华: 0
发帖: 437
可可豆: 24891 CB
威望: 25992 点
在线时间: 3388(时)
注册时间: 2009-04-02
最后登录: 2018-08-21
9 楼:  发表于: 2014-01-03 10:12    发自: Web Page
多谢分享!
一直在努力,一直都觉得努力得还不够~~
www.etware.com

CocoaChina社区转载内容已尽可能注明出处,如未能核实来源或转发内容图片有权利瑕疵的,请及时联系社区进行修改或删除【联系方式QQ : 3442093904 邮箱:support@cocoachina.com】文章内容为作者独立观点,不代表CocoaChina社区立场。版权归原作者所有,如申请授权请联系作者,因文章侵权CocoaChina社区不承担任何法律及连带责任。

描述
快速回复

关注本帖(如果有新回复会站内信通知您)

发帖、回帖都会得到可观的积分奖励。查看论坛积分规则

按"Ctrl+Enter"直接提交
    顶部