美文网首页
OpenGL ES - 粒子动画

OpenGL ES - 粒子动画

作者: 再好一点点 | 来源:发表于2021-08-18 09:55 被阅读0次

    先看效果

    image

    因后面所有关于opengl的demo都是基于现有的一套框架的,所以需要在这第一篇文章中简单介绍一下基类

    BaseViewController.h

    #import <GLKit/GLKit.h>
    #import <OpenGLES/ES3/gl.h>
    #include "esUtil.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface BaseViewController : GLKViewController {
        ESContext _esContext;
    }
    
    @property (strong, nonatomic) EAGLContext *context;
    
    - (void)setupGL;
    
    @end
    

    BaseViewController.m

    #import "BaseViewController.h"
    
    @implementation BaseViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.view.backgroundColor = [UIColor whiteColor];
    
        self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    
        if (!self.context)
        {
            NSLog(@"Failed to create ES context");
        }
    
        GLKView *view = (GLKView *)self.view;
        view.context = self.context;
        view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    
    }
    
    - (void)dealloc
    {
        [self tearDownGL];
    
        if ([EAGLContext currentContext] == self.context) {
            [EAGLContext setCurrentContext:nil];
        }
    }
    
    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
    
        if ([self isViewLoaded] && ([[self view] window] == nil)) {
            self.view = nil;
    
            [self tearDownGL];
    
            if ([EAGLContext currentContext] == self.context) {
                [EAGLContext setCurrentContext:nil];
            }
            self.context = nil;
        }
    
    }
    
    - (void)setupGL
    {
        [EAGLContext setCurrentContext:self.context];
        memset( &_esContext, 0, sizeof( _esContext ) );
    }
    
    - (void)tearDownGL
    {
        [EAGLContext setCurrentContext:self.context];
    
        if ( _esContext.shutdownFunc )
        {
            _esContext.shutdownFunc( &_esContext );
        }
    }
    
    @end
    

    然后是一个列表页,用来展示所有的demo的列表,ViewController.h

    @interface ViewController : UIViewController
    
    @end
    

    接着是ViewController.m

    #import "ViewController.h"
    
    @interface ViewController ()<UITableViewDelegate, UITableViewDataSource>
    
    @property (weak, nonatomic) IBOutlet UITableView *tableView;
    @property (strong, nonatomic) NSMutableArray<NSString *> *titleArray;
    @property (strong, nonatomic) NSMutableArray<NSString *> *vcArray;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
    
        self.titleArray = [NSMutableArray arrayWithObjects:@"粒子动画", @"双重纹理", @"图片模糊", nil];
        self.vcArray = [NSMutableArray arrayWithObjects:@"ParticleSystemVCViewController", @"DoubleSampleVC", @"BlurViewController", nil];
    
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return _titleArray.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"identifier"];
        if (!cell) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"identifier"];
            cell.textLabel.text = _titleArray[indexPath.row];
        }
        return cell;
    }
    
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        Class class = NSClassFromString(_vcArray[indexPath.row]);
        UIViewController *vc = [class new];
        vc.title = _titleArray[indexPath.row];
        [self.navigationController pushViewController:vc animated:YES];
    }
    
    @end
    

    以上介绍了一下整个项目的大致结构。下面进入今天的正题粒子动画

    ParticleSystemVCViewController继承自BaseViewController

    #import "BaseViewController.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface ParticleSystemVCViewController : BaseViewController
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    #import "ParticleSystemVCViewController.h"
    
     extern  void esParticleSystemMain( ESContext *esContext );
    
    @interface ParticleSystemVCViewController ()
    
    @end
    
    @implementation ParticleSystemVCViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self setupGL];
    }
    
    - (void)setupGL
    {
        [super setupGL];
    
        esParticleSystemMain( &_esContext );
    }
    
    - (void)update
    {
        if ( _esContext.updateFunc )
        {
            _esContext.updateFunc( &_esContext, self.timeSinceLastUpdate );
        }
    }
    
    - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
    {
        _esContext.width = view.drawableWidth;
        _esContext.height = view.drawableHeight;
    
        if ( _esContext.drawFunc )
        {
            _esContext.drawFunc( &_esContext );
        }
    }
    
    @end
    

    OC代码准备完成,接下来就是核心部分。

    加载shader以及获取句柄

    ///
    // Initialize the shader and program object
    //
    static int Init ( ESContext *esContext )
    {
       UserData *userData = esContext->userData;
       int i;
    
       char vShaderStr[] =
          "#version 300 es                                      \n"
          "uniform float u_time;                                \n"
          "uniform vec3 u_centerPosition;                       \n"
          "layout(location = 0) in float a_lifetime;            \n"
          "layout(location = 1) in vec3 a_startPosition;        \n"
          "layout(location = 2) in vec3 a_endPosition;          \n"
          "out float v_lifetime;                                \n"
          "void main()                                          \n"
          "{                                                    \n"
          "  if ( u_time <= a_lifetime )                        \n"
          "  {                                                  \n"
          "    gl_Position.xyz = a_startPosition +              \n"
          "                      (u_time * a_endPosition);      \n"
          "    gl_Position.xyz += u_centerPosition;             \n"
          "    gl_Position.w = 1.0;                             \n"
          "  }                                                  \n"
          "  else                                               \n"
          "  {                                                  \n"
          "     gl_Position = vec4( -1000, -1000, 0, 0 );       \n"
          "  }                                                  \n"
          "  v_lifetime = 1.0 - ( u_time / a_lifetime );        \n"
          "  v_lifetime = clamp ( v_lifetime, 0.0, 1.0 );       \n"
          "  gl_PointSize = ( v_lifetime * v_lifetime ) * 40.0; \n"
          "}";
    
       char fShaderStr[] =
          "#version 300 es                                      \n"
          "precision mediump float;                             \n"
          "uniform vec4 u_color;                                \n"
          "in float v_lifetime;                                 \n"
          "layout(location = 0) out vec4 fragColor;             \n"
          "uniform sampler2D s_texture;                         \n"
          "void main()                                          \n"
          "{                                                    \n"
          "  vec4 texColor;                                     \n"
          "  texColor = texture( s_texture, gl_PointCoord );    \n"
          "  fragColor = vec4( u_color ) * texColor;            \n"
          "  fragColor.a *= v_lifetime;                         \n"
          "}                                                    \n";
    
       // Load the shaders and get a linked program object
       userData->programObject = esLoadProgram ( vShaderStr, fShaderStr );
    
       // Get the uniform locations
       userData->timeLoc = glGetUniformLocation ( userData->programObject, "u_time" );
       userData->centerPositionLoc = glGetUniformLocation ( userData->programObject, "u_centerPosition" );
       userData->colorLoc = glGetUniformLocation ( userData->programObject, "u_color" );
       userData->samplerLoc = glGetUniformLocation ( userData->programObject, "s_texture" );
    
       glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );
    
       // Fill in particle data array
       srand ( 0 );
    
       for ( i = 0; i < NUM_PARTICLES; i++ )
       {
          float *particleData = &userData->particleData[i * PARTICLE_SIZE];
    
          // Lifetime of particle
           //0 - 1
          ( *particleData++ ) = ( ( float ) ( rand() % 10000 ) / 10000.0f );
    
           //0 - 1
          // End position of particle
           //-1 - 1
          ( *particleData++ ) = ( ( float ) ( rand() % 10000 ) / 5000.0f ) - 1.0f;
          ( *particleData++ ) = ( ( float ) ( rand() % 10000 ) / 5000.0f ) - 1.0f;
          ( *particleData++ ) = ( ( float ) ( rand() % 10000 ) / 5000.0f ) - 1.0f;
    
          // Start position of particle
           //-0.125 - 0.125
          ( *particleData++ ) = ( ( float ) ( rand() % 10000 ) / 40000.0f ) - 0.125f;
          ( *particleData++ ) = ( ( float ) ( rand() % 10000 ) / 40000.0f ) - 0.125f;
          ( *particleData++ ) = ( ( float ) ( rand() % 10000 ) / 40000.0f ) - 0.125f;
    
       }
    
       // Initialize time to cause reset on first update
       userData->time = 1.0f;
    
       userData->textureId = LoadTexture ( esContext->platformData, "smoke.tga" );
    
       if ( userData->textureId <= 0 )
       {
          return FALSE;
       }
    
       return TRUE;
    }
    

    继承至GLKViewController的类每帧都会执行以下两个方法,在初始化context的时候指定了函数指针,所以每帧都会执行ParticleSystem.c类里边的update和drwa函数

    - (void)update
    - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
    

    函数static void Update ( ESContext *esContext, float deltaTime ),这里设置粒子的中心点以及起始颜色。

    ///
    //  Update time-based variables
    //
    static void Update ( ESContext *esContext, float deltaTime )
    {
       UserData *userData = esContext->userData;
    
       userData->time += deltaTime;
    
       glUseProgram ( userData->programObject );
    
       if ( userData->time >= 1.0f )
       {
          float centerPos[3];
          float color[4];
    
          userData->time = 0.0f;
    
          // Pick a new start location and color
           //-0.5 - 0.5
          centerPos[0] = ( ( float ) ( rand() % 10000 ) / 10000.0f ) - 0.5f;
          centerPos[1] = ( ( float ) ( rand() % 10000 ) / 10000.0f ) - 0.5f;
          centerPos[2] = ( ( float ) ( rand() % 10000 ) / 10000.0f ) - 0.5f;
    
          glUniform3fv ( userData->centerPositionLoc, 1, &centerPos[0] );
    
          // Random color
           //0.5 - 1
          color[0] = ( ( float ) ( rand() % 10000 ) / 20000.0f ) + 0.5f;
          color[1] = ( ( float ) ( rand() % 10000 ) / 20000.0f ) + 0.5f;
          color[2] = ( ( float ) ( rand() % 10000 ) / 20000.0f ) + 0.5f;
          color[3] = 0.5;
    
          glUniform4fv ( userData->colorLoc, 1, &color[0] );
       }
    
       // Load uniform time variable
       glUniform1f ( userData->timeLoc, userData->time );
    }
    

    函数static void Draw ( ESContext *esContext )设置窗口大小位置以及每个粒子在当前时间点的起始位置以及颜色。

    ///
    // Draw a triangle using the shader pair created in Init()
    //
    static void Draw ( ESContext *esContext )
    {
       UserData *userData = esContext->userData;
    
       // Set the viewport
       glViewport ( 0, 0, esContext->width, esContext->height );
    
       // Clear the color buffer
       glClear ( GL_COLOR_BUFFER_BIT );
    
       // Use the program object
       glUseProgram ( userData->programObject );
    
       // Load the vertex attributes
       glVertexAttribPointer ( ATTRIBUTE_LIFETIME_LOCATION, 1, GL_FLOAT,
                               GL_FALSE, PARTICLE_SIZE * sizeof ( GLfloat ),
                               userData->particleData );
    
       glVertexAttribPointer ( ATTRIBUTE_ENDPOSITION_LOCATION, 3, GL_FLOAT,
                               GL_FALSE, PARTICLE_SIZE * sizeof ( GLfloat ),
                               &userData->particleData[1] );
    
       glVertexAttribPointer ( ATTRIBUTE_STARTPOSITION_LOCATION, 3, GL_FLOAT,
                               GL_FALSE, PARTICLE_SIZE * sizeof ( GLfloat ),
                               &userData->particleData[4] );
    
       glEnableVertexAttribArray ( ATTRIBUTE_LIFETIME_LOCATION );
       glEnableVertexAttribArray ( ATTRIBUTE_ENDPOSITION_LOCATION );
       glEnableVertexAttribArray ( ATTRIBUTE_STARTPOSITION_LOCATION );
    
       // Blend particles
       glEnable ( GL_BLEND );
       glBlendFunc ( GL_SRC_ALPHA, GL_ONE );
    
       // Bind the texture
       glActiveTexture ( GL_TEXTURE0 );
       glBindTexture ( GL_TEXTURE_2D, userData->textureId );
    
       // Set the sampler texture unit to 0
       glUniform1i ( userData->samplerLoc, 0 );
    
       glDrawArrays ( GL_POINTS, 0, NUM_PARTICLES );
    }
    

    可以下载demo运行一下,看看具体效果。

    所有demo地址

    本人原文博客地址

    相关文章

      网友评论

          本文标题:OpenGL ES - 粒子动画

          本文链接:https://www.haomeiwen.com/subject/dsfubltx.html