美文网首页
GLSL_粒子动画

GLSL_粒子动画

作者: tp夕阳武士 | 来源:发表于2020-04-30 11:41 被阅读0次
1.顶点着色器的构建

1.构建顶点着色器时需要传入的参数


attribute vec3 a_emissionPosition; //位置
attribute vec3 a_emissionVelocity; //速度
attribute vec3 a_emissionForce;    //受力
attribute vec2 a_size;             //大小 和 Fade持续时间  size = GLKVector2Make(aSize, aDuration);
attribute vec2 a_emissionAndDeathTimes; //发射时间 和 消失时间

// UNIFORMS
uniform highp mat4      u_mvpMatrix;      //变换矩阵
uniform sampler2D       u_samplers2D[1];  //纹理
uniform highp vec3      u_gravity;        //重力
uniform highp float     u_elapsedSeconds; //当前时间

//这个参数是用于与片元着色器之间传递参数
varying lowp float      v_particleOpacity;   //粒子透明度

2.顶点着色器main()函数

void main()
{
    
    //流逝时间
    highp float elapsedTime = u_elapsedSeconds - a_emissionAndDeathTimes.x;
    
    // 质量假设是1.0 加速度 = 力 (a = f/m)
    // v = v0 + at : v 是当前速度; v0 是初速度;
    //               a 是加速度; t 是时间
    //a_emissionForce 受力,u_gravity 重力
    
    //求速度velocity
    highp vec3 velocity =   +
    ((a_emissionForce + u_gravity) * elapsedTime);
    
    // s = s0 + 0.5 * (v0 + v) * t
    //                              s 当前位置
    //                              s0 初始位置
    //                              v0 初始速度
    //                              v 当前速度
    //                              t 是时间
    
    // 运算是对向量运算,相当于分别求出x、y、z的位置,再综合
    //求粒子的受力后的位置 = a_emissionPosition(原始位置) + 0.5 * (速度+加速度) * 流逝时间

    highp vec3 untransformedPosition = a_emissionPosition +
    0.5 * (a_emissionVelocity + velocity) * elapsedTime;
    
    //得出点的位置
    gl_Position = u_mvpMatrix * vec4(untransformedPosition, 1.0);
    gl_PointSize = a_size.x / gl_Position.w;
    
    
    // 消失时间减去当前时间,得到当前的寿命; 除以Fade持续时间,当剩余时间小于Fade时间后,得到一个从1到0变化的值
    // 如果这个值小于0,则取0
    
    //a_emissionAndDeathTimes vec2类型 (发射时间,消失时间)
    //u_elapsedSeconds float类型 消失时间
    //a_size vec2类型 (大小,frade持续时间); 所以a_size.y = 持续时间
    // v_particleOpacity 粒子透明度, 透明度的取值范围应该是(0-1);
    float remainTime = a_emissionAndDeathTimes.y - u_elapsedSeconds; //剩余声明
    float keepTime = max(a_size.y, 0.00001);
    v_particleOpacity = max(0.0, min(1.0,remainTime / keepTime));
}
2.片元着色器的构建

1.接受参数

// UNIFORMS
uniform highp mat4      u_mvpMatrix;
uniform sampler2D       u_samplers2D[1];
uniform highp vec3      u_gravity;
uniform highp float     u_elapsedSeconds;

// Varyings
varying lowp float      v_particleOpacity;

2.main()函数

void main()
{
   
    //http://blog.csdn.net/hgl868/article/details/7876246
    //通过texture2D函数我们可以得到一个纹素(texel),这是一个纹理图片中的像素。函数参数分别为simpler2D以及纹理坐标:
    // gl_PointCoord是片元着色器的内建只读变量,它的值是当前片元所在点图元的二维坐标。点的范围是0.0到1.0
    lowp vec4 textureColor = texture2D(u_samplers2D[0], gl_PointCoord);
    
    
    //粒子透明度 与 v_particleOpacity 值相关
    textureColor.a = textureColor.a * v_particleOpacity; // 在原基础上的透明度乘以透明因子;
    
    //设置片元颜色值
    gl_FragColor = textureColor;
}
3.粒子构造器

1.粒子构造器.h文件

#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>

/////////////////////////////////////////////////////////////////
// 默认重力加速度矢量与地球的
// {0, (-9.80665 m/s/s), 0} assuming +Y up coordinate system
extern const GLKVector3 CCDefaultGravity;

@interface CCPointParticleEffect : NSObject

//重力
@property(nonatomic,assign)GLKVector3 gravity;

//耗时
@property(nonatomic,assign)GLfloat elapsedSeconds;

//纹理
@property (strong, nonatomic, readonly)GLKEffectPropertyTexture *texture2d0;

//变换
@property (strong, nonatomic, readonly) GLKEffectPropertyTransform *transform;


//添加粒子
/*
 aPosition:位置
 aVelocity:速度
 aForce:重力
 aSize:大小
 aSpan:跨度
 aDuration:时长
 */
- (void)addParticleAtPosition:(GLKVector3)aPosition
                     velocity:(GLKVector3)aVelocity
                        force:(GLKVector3)aForce
                         size:(float)aSize
              lifeSpanSeconds:(NSTimeInterval)aSpan
          fadeDurationSeconds:(NSTimeInterval)aDuration;

//准备绘制
- (void)prepareToDraw;

//绘制
- (void)draw;

@end

2.粒子构造器.m文件

#import "CCPointParticleEffect.h"
#import "CCVertexAttribArrayBuffer.h"
//用于定义粒子属性的类型
typedef struct
{
    GLKVector3 emissionPosition;//发射位置
    GLKVector3 emissionVelocity;//发射速度
    GLKVector3 emissionForce;//发射重力
    GLKVector2 size;//发射大小
    GLKVector2 emissionTimeAndLife;//发射时间和寿命
}CCParticleAttributes;

//GLSL程序Uniform 参数
enum
{
    CCMVPMatrix,//MVP矩阵
    CCSamplers2D,//Samplers2D纹理
    CCElapsedSeconds,//耗时
    CCGravity,//重力
    CCNumUniforms//Uniforms个数  CCNumUniforms == 4;
};



//属性标识符
typedef enum {
    CCParticleEmissionPosition = 0,//粒子发射位置
    CCParticleEmissionVelocity,//粒子发射速度
    CCParticleEmissionForce,//粒子发射重力
    CCParticleSize,//粒子发射大小
    CCParticleEmissionTimeAndLife,//粒子发射时间和寿命
} CCParticleAttrib;

//默认重力加速度向量与地球的匹配
//{ 0,(-9.80665米/秒/秒),0 }假设+ Y坐标系的建立
//默认重力
const GLKVector3 CCDefaultGravity = {0.0f, -9.80665f, 0.0f};

@interface CCPointParticleEffect()
{
    GLfloat elapsedSeconds;//耗时
    GLuint program;//程序
    GLint uniforms[CCNumUniforms];//Uniforms数组
}

//顶点属性数组缓冲区
@property (strong, nonatomic, readwrite)CCVertexAttribArrayBuffer  * particleAttributeBuffer;

//粒子个数
@property (nonatomic, assign, readonly) NSUInteger numberOfParticles;

//粒子属性数据
@property (nonatomic, strong, readonly) NSMutableData *particleAttributesData;

//是否更新粒子数据
@property (nonatomic, assign, readwrite) BOOL particleDataWasUpdated;

//加载shaders
- (BOOL)loadShaders;

//编译shaders
- (BOOL)compileShader:(GLuint *)shader
                 type:(GLenum)type
                 file:(NSString *)file;
//链接Program
- (BOOL)linkProgram:(GLuint)prog;

//验证Program
- (BOOL)validateProgram:(GLuint)prog;

@end

@implementation CCPointParticleEffect

@synthesize gravity;
@synthesize elapsedSeconds;
@synthesize texture2d0;
@synthesize transform;
@synthesize particleAttributeBuffer;
@synthesize particleAttributesData;
@synthesize particleDataWasUpdated;

//初始化
-(id)init
{
    self = [super init];
    if (self != nil) {
        
        //1.初始化纹理属性
        //初始化
        texture2d0 = [[GLKEffectPropertyTexture alloc] init];
        //是否可用
        texture2d0.enabled = YES;
        //命名纹理对象
        /*
         等价于:
         void glGenTextures (GLsizei n, GLuint *textures);
         //在数组textures中返回n个当期未使用的值,表示纹理对象的名称
         //零作为一个保留的纹理对象名,它不会被此函数当做纹理对象名称而返回
         */
        texture2d0.name = 0;
        
        //纹理类型 默认值是glktexturetarget2d
        texture2d0.target = GLKTextureTarget2D;
        //纹理用于计算其输出片段颜色的模式。看到GLKTextureEnvMode。
        /*
         GLKTextureEnvModeReplace,输出颜色设置为从纹理获取的颜色。忽略输入颜色
         GLKTextureEnvModeModulate, 默认!输出颜色是通过将纹理的颜色乘以输入颜色来计算的
         GLKTextureEnvModeDecal,输出颜色是通过使用纹理的alpha组件来混合纹理颜色和输入颜色来计算的。
         */
        texture2d0.envMode = GLKTextureEnvModeReplace;
        
        //坐标变换的信息用于GLKit渲染效果。GLKEffectPropertyTransform类定义的属性进行渲染时的效果提供的坐标变换。
        transform = [[GLKEffectPropertyTransform alloc] init];
        
        //重力.默认地球重力
        gravity = CCDefaultGravity;
        
        //耗时
        elapsedSeconds = 0.0f;
        
        //粒子属性数据
        particleAttributesData = [NSMutableData data];

    }
    
    return self;
}



//获取粒子的属性值
- (CCParticleAttributes)particleAtIndex:(NSUInteger)anIndex
{
    
    //bytes:指向接收者内容的指针
    //获取粒子属性结构体内容
    const CCParticleAttributes *particlesPtr = (const CCParticleAttributes *)[self.particleAttributesData bytes];
    

    
    //获取属性结构体中的某一个指标
    return particlesPtr[anIndex];
}


//设置粒子的属性
- (void)setParticle:(CCParticleAttributes)aParticle
            atIndex:(NSUInteger)anIndex
{
    //mutableBytes:指向可变数据对象所包含数据的指针
    //获取粒子属性结构体内容
    CCParticleAttributes *particlesPtr = (CCParticleAttributes *)[self.particleAttributesData mutableBytes];
    
    //将粒子结构体对应的属性修改为新值
    particlesPtr[anIndex] = aParticle;
    
    //更改粒子状态! 是否更新
    self.particleDataWasUpdated = YES;
}

//添加一个粒子
- (void)addParticleAtPosition:(GLKVector3)aPosition
                     velocity:(GLKVector3)aVelocity
                        force:(GLKVector3)aForce
                         size:(float)aSize
              lifeSpanSeconds:(NSTimeInterval)aSpan
          fadeDurationSeconds:(NSTimeInterval)aDuration;
{
    //创建新的例子
    CCParticleAttributes newParticle;
    
    //设置相关参数(位置\速度\抛物线\大小\耗时)
    newParticle.emissionPosition = aPosition;
    newParticle.emissionVelocity = aVelocity;
    newParticle.emissionForce = aForce;
    newParticle.size = GLKVector2Make(aSize, aDuration);
    //向量(耗时,发射时长)
    newParticle.emissionTimeAndLife = GLKVector2Make(elapsedSeconds, elapsedSeconds + aSpan);
    
    BOOL foundSlot = NO;
    
    //粒子个数
    const long count = self.numberOfParticles;
    
    //循环设置粒子到数组中
    for(int i = 0; i < count && !foundSlot; i++)
    {
        
        //获取当前旧的粒子
        CCParticleAttributes oldParticle = [self particleAtIndex:i];
        
        //如果旧的例子的发射时长 小于 耗时
        //emissionTimeAndLife.y = elapsedSeconds + aspan
        if(oldParticle.emissionTimeAndLife.y < self.elapsedSeconds)
        {
            //更新例子的属性
            [self setParticle:newParticle atIndex:i];
            
            //是否替换
            foundSlot = YES;
        }
    }
    
    //如果不替换
    if(!foundSlot)
    {
        //在particleAttributesData 拼接新的数据
        [self.particleAttributesData appendBytes:&newParticle
                                          length:sizeof(newParticle)];
        
        //粒子数据是否更新
        self.particleDataWasUpdated = YES;
    }
}

//获取粒子个数
- (NSUInteger)numberOfParticles;
{
    static long last;
    //总数据/粒子结构体大小
    long ret = [self.particleAttributesData length] / sizeof(CCParticleAttributes);
    
    //如果last != ret 表示粒子个数更新了
    if (last != ret) {
        //则修改last数量
        last = ret;
        NSLog(@"count %ld", ret);
    }
    
    return ret;
}


- (void)prepareToDraw
{
    //program =0 ,则加载shaders
    if(program == 0)
    {
        //加载shaders(顶点\片元)
        [self loadShaders];
    }
    
    if(program != 0)
    {
        //使用program
        glUseProgram(program);
        
        // 计算MVP矩阵变化
        //投影矩阵 与 模式视图矩阵 相乘结果
        GLKMatrix4 modelViewProjectionMatrix = GLKMatrix4Multiply(self.transform.projectionMatrix,self.transform.modelviewMatrix);
        //将结果矩阵,通过unifrom传递
        /*
         glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
        参数1:location,要更改的uniforms变量的位置
        参数2:cout ,更改矩阵的个数
        参数3:transpose,指是否要转置矩阵,并将它作为uniform变量的值,必须为GL_FALSE
        参数4:value ,指向count个数的元素指针.用来更新uniform变量的值.
        */

        glUniformMatrix4fv(uniforms[CCMVPMatrix], 1, 0,modelViewProjectionMatrix.m);
        
        // 一个纹理采样均匀变量
        /*
          glUniform1f(GLint location,  GLfloat v0); 
         
         location:指明要更改的uniform变量的位置
         v0:指明在指定的uniform变量中要使用的新值
         */
        glUniform1i(uniforms[CCSamplers2D], 0);
        
        //粒子物理值
        //重力
        /*
         void glUniform3fv(GLint location,  GLsizei count,  const GLfloat *value); 
         参数列表:
         location:指明要更改的uniform变量的位置
         count:指明要更改的向量个数
         value:指明一个指向count个元素的指针,用来更新指定的uniform变量。
         
         */
        glUniform3fv(uniforms[CCGravity], 1, self.gravity.v);
        
        //耗时
        glUniform1fv(uniforms[CCElapsedSeconds], 1, &elapsedSeconds);
        
        //粒子数据更新
        if(self.particleDataWasUpdated)
        {
            //缓存区为空,且粒子数据大小>0
            if(self.particleAttributeBuffer == nil && [self.particleAttributesData length] > 0)
                
            {  // 顶点属性没有送到GPU
                //初始化缓存区
                /*
                  1.数据大小  sizeof(CCParticleAttributes)
                  2.数据个数 (int)[self.particleAttributesData length] /
                 sizeof(CCParticleAttributes)
                  3.数据源  [self.particleAttributesData bytes]
                  4.用途 GL_DYNAMIC_DRAW
                 */
                
                // 一个粒子的大小
                GLsizeiptr size = sizeof(CCParticleAttributes);
                // 粒子的个数
                int count = (int)[self.particleAttributesData length] /
                sizeof(CCParticleAttributes);
                
                //开辟顶点数据的缓存空间
                self.particleAttributeBuffer =
                [[CCVertexAttribArrayBuffer alloc]
                 initWithAttribStride:size
                 numberOfVertices:count
                 bytes:[self.particleAttributesData bytes]
                 usage:GL_DYNAMIC_DRAW];
            }
            else
            {
                //如果已经开辟空间,则接收新的数据
                /*
                 1.数据大小 sizeof(CCParticleAttributes)
                 2.数据个数  (int)[self.particleAttributesData length] /
                 sizeof(CCParticleAttributes)
                 3.数据源 [self.particleAttributesData bytes]
                 */
                
                //数据大小
                GLsizeiptr size = sizeof(CCParticleAttributes);
                //个数
                int count = (int)[self.particleAttributesData length] /
                sizeof(CCParticleAttributes);
                
                [self.particleAttributeBuffer
                 reinitWithAttribStride:size
                 numberOfVertices:count
                 bytes:[self.particleAttributesData bytes]];
            }
            
            //恢复更新状态为NO
            self.particleDataWasUpdated = NO;
        }
        
        //准备顶点数据
        [self.particleAttributeBuffer
         prepareToDrawWithAttrib:CCParticleEmissionPosition
         numberOfCoordinates:3
         attribOffset:
         offsetof(CCParticleAttributes, emissionPosition)
         shouldEnable:YES];
        
        //准备粒子发射速度数据
        [self.particleAttributeBuffer
         prepareToDrawWithAttrib:CCParticleEmissionVelocity
         numberOfCoordinates:3
         attribOffset:
         offsetof(CCParticleAttributes, emissionVelocity)
         shouldEnable:YES];
        
        //准备重力数据
        [self.particleAttributeBuffer
         prepareToDrawWithAttrib:CCParticleEmissionForce
         numberOfCoordinates:3
         attribOffset:
         offsetof(CCParticleAttributes, emissionForce)
         shouldEnable:YES];
        
        //准备粒子size数据
        [self.particleAttributeBuffer
         prepareToDrawWithAttrib:CCParticleSize
         numberOfCoordinates:2
         attribOffset:
         offsetof(CCParticleAttributes, size)
         shouldEnable:YES];
        
        //准备粒子的持续时间和渐隐时间数据
        [self.particleAttributeBuffer
         prepareToDrawWithAttrib:CCParticleEmissionTimeAndLife
         numberOfCoordinates:2
         attribOffset:
         offsetof(CCParticleAttributes, emissionTimeAndLife)
         shouldEnable:YES];
        
        //将所有纹理绑定到各自的单位
        /*
         void glActiveTexture(GLenum texUnit);
         
         该函数选择一个纹理单元,线面的纹理函数将作用于该纹理单元上,参数为符号常量GL_TEXTUREi ,i的取值范围为0~K-1,K是OpenGL实现支持的最大纹理单元数,可以使用GL_MAX_TEXTURE_UNITS来调用函数glGetIntegerv()获取该值
         
         可以这样简单的理解为:显卡中有N个纹理单元(具体数目依赖你的显卡能力),每个纹理单元(GL_TEXTURE0、GL_TEXTURE1等)都有GL_TEXTURE_1D、GL_TEXTURE_2D等
         */
        glActiveTexture(GL_TEXTURE0);
        
        //判断纹理标记是否为空,以及纹理是否可用
        if(0 != self.texture2d0.name && self.texture2d0.enabled)
        {
            //绑定纹理到纹理标记上
            //参数1:纹理类型
            //参数2:纹理名称
            glBindTexture(GL_TEXTURE_2D, self.texture2d0.name);
        }
        else
        {
            //绑定一个空的
            glBindTexture(GL_TEXTURE_2D, 0);
        }
    }
}

//绘制
- (void)draw;
{
    //禁用深度缓冲区写入
    glDepthMask(GL_FALSE);
    
    //绘制
    /*
     1.模式
     2.开始的位置
     3.粒子个数
     */
    [self.particleAttributeBuffer drawArrayWithMode:GL_POINTS startVertexIndex:0 numberOfVertices:(int)self.numberOfParticles];
    
    //启用深度缓冲区写入
    glDepthMask(GL_TRUE);
}

#pragma mark -  OpenGL ES shader compilation

- (BOOL)loadShaders
{
    GLuint vertShader, fragShader;
    NSString *vertShaderPathname, *fragShaderPathname;
    
    //创建program
    program = glCreateProgram();
    
    //创建并编译 vertex shader.
    vertShaderPathname = [[NSBundle mainBundle] pathForResource:
                          @"CCPointParticleShader" ofType:@"vsh"];
    if (![self compileShader:&vertShader type:GL_VERTEX_SHADER
                        file:vertShaderPathname])
    {
        NSLog(@"Failed to compile vertex shader");
        return NO;
    }
    
    // 创建并编译 fragment shader.
    fragShaderPathname = [[NSBundle mainBundle] pathForResource:
                          @"CCPointParticleShader" ofType:@"fsh"];
    if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER
                        file:fragShaderPathname])
    {
        NSLog(@"Failed to compile fragment shader");
        return NO;
    }
    
    //将vertex shader 附加到程序.
    glAttachShader(program, vertShader);
    
    //将fragment shader 附加到程序.
    glAttachShader(program, fragShader);
    
    //绑定属性位置
    //这需要在链接之前完成.
    /*
     应用程序通过glBindAttribLocation把“顶点属性索引”绑定到“顶点属性名”,glBindAttribLocation在program被link之前执行。
     void glBindAttribLocation(GLuint program, GLuint index,const GLchar *name)  
     program:对应的程序
     index:顶点属性索引
     name:属性名称
     */
    
    //位置
    glBindAttribLocation(program, CCParticleEmissionPosition,
                         "a_emissionPosition");
    //速度
    glBindAttribLocation(program, CCParticleEmissionVelocity,
                         "a_emissionVelocity");
    //重力
    glBindAttribLocation(program, CCParticleEmissionForce,
                         "a_emissionForce");
    //大小
    glBindAttribLocation(program, CCParticleSize,
                         "a_size");
    //持续时间、渐隐时间
    glBindAttribLocation(program, CCParticleEmissionTimeAndLife,
                         "a_emissionAndDeathTimes");
    
    // Link program 失败
    if (![self linkProgram:program])
    {
        NSLog(@"Failed to link program: %d", program);
        
        //link识别,删除vertex shader\fragment shader\program
        if (vertShader)
        {
            glDeleteShader(vertShader);
            vertShader = 0;
        }
        if (fragShader)
        {
            glDeleteShader(fragShader);
            fragShader = 0;
        }
        if (program)
        {
            glDeleteProgram(program);
            program = 0;
        }
        
        return NO;
    }
    
    // 获取uniform变量的位置.
    //MVP变换矩阵
    uniforms[CCMVPMatrix] = glGetUniformLocation(program,"u_mvpMatrix");
    //纹理
    uniforms[CCSamplers2D] = glGetUniformLocation(program,"u_samplers2D");
    //重力
    uniforms[CCGravity] = glGetUniformLocation(program,"u_gravity");
    //持续时间、渐隐时间
    uniforms[CCElapsedSeconds] = glGetUniformLocation(program,"u_elapsedSeconds");
    
    //使用完
    // 删除 vertex and fragment shaders.
    if (vertShader)
    {
        glDetachShader(program, vertShader);
        glDeleteShader(vertShader);
    }
    if (fragShader)
    {
        glDetachShader(program, fragShader);
        glDeleteShader(fragShader);
    }
    
    return YES;
}


//编译shader
- (BOOL)compileShader:(GLuint *)shader
                 type:(GLenum)type
                 file:(NSString *)file
{
    //状态
    GLint status;
    //路径-C语言
    const GLchar *source;
    
    //从OC字符串中获取C语言字符串
    //获取路径
    source = (GLchar *)[[NSString stringWithContentsOfFile:file
                                                  encoding:NSUTF8StringEncoding error:nil] UTF8String];
    //判断路径
    if (!source)
    {
        NSLog(@"Failed to load vertex shader");
        return NO;
    }
    
    //创建shader-顶点\片元
    *shader = glCreateShader(type);
    
    //绑定shader
    glShaderSource(*shader, 1, &source, NULL);
   
    //编译Shader
    glCompileShader(*shader);
    
    //获取加载Shader的日志信息
    //日志信息长度
    GLint logLength;
    /*
     在OpenGL中有方法能够获取到 shader错误
     参数1:对象,从哪个Shader
     参数2:获取信息类别,
     GL_COMPILE_STATUS       //编译状态
     GL_INFO_LOG_LENGTH      //日志长度
     GL_SHADER_SOURCE_LENGTH //着色器源文件长度
     GL_SHADER_COMPILER  //着色器编译器
     参数3:获取长度
     */
    glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
    
    //判断日志长度 > 0
    if (logLength > 0)
    {
        //创建日志字符串
        GLchar *log = (GLchar *)malloc(logLength);
       
        /*
         获取日志信息
         参数1:着色器
         参数2:日志信息长度
         参数3:日志信息长度地址
         参数4:日志存储的位置
         */
        glGetShaderInfoLog(*shader, logLength, &logLength, log);
       
        //打印日志信息
        NSLog(@"Shader compile log:\n%s", log);
      
        //释放日志字符串
        free(log);
        return NO;
    }

    
    return YES;
}

//链接program
- (BOOL)linkProgram:(GLuint)prog
{
    //状态
    GLint status;
    //链接Programe
    glLinkProgram(prog);
    //打印链接program的日志信息
    GLint logLength;
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program link log:\n%s", log);
        free(log);
        
        return NO;
    }
    
    return YES;
}

//验证Program
- (BOOL)validateProgram:(GLuint)prog
{
    //日志长度,验证状态
    GLint logLength, status;
    
    //验证prgogram
    //http://www.dreamingwish.com/frontui/article/default/glvalidateprogram.html
    /*
     检测program中包含的执行段在给定的当前OpenGL状态下是否可执行。
    验证过程产生的信息会被存储在program日志中。
    验证信息可能由一个空字符串组成,
    或者可能是一个包含当前程序对象如何与余下的OpenGL当前状态交互的信息的字符串。
   这为OpenGL实现提供了一个方法来调查更多关于程序效率低下、低优化、执行失败等的信息。
   验证操作的结果状态值会被存储为程序对象状态的一部分这个值会  
   被置为GL_TURE或GL_FALSE。
   调用函数 glGetProgramiv 传入参数 program和GL_VALIDATE_STATUS可以查询这个值。
   在给定当前状态下,如果验证成功,那么 program保证可以执行,反之保证不会执行
   GL_INVALID_VALUE 错误:如果 program 不是由 OpenGL生成的值.
     GL_INVALID_OPERATION 错误:如果 program 不是一个程序对象.
     */
    glValidateProgram(prog);
    
    //获取验证的日志信息
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program validate log:\n%s", log);
        free(log);
    }
    
    //获取验证的状态--验证结果
    glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
    //根据验证结果返回NO or YES
    if (status == 0)
    {
        return NO;
    }
    
    return YES;
}

@end

注意,上文的代码中使用了封装好的工具类,CCVertexAttribArrayBuffer,这个工具的主要作用是生成缓冲区,绑定缓冲区,绘制图形;

4.构建4中不同粒子效果的block
//执行代码块.4种不同效果.
    void(^blockA)(void) = ^{

        self.autoSpawnDelta = 0.5f;

        //重力
        self.particleEffect.gravity = CCDefaultGravity;

        //X轴上随机速度
        float randomXVelocity = -0.5f + 1.0f * (float)random() / (float)RAND_MAX;

        /*
         Position:出发位置
         velocity:速度
         force:抛物线
         size:大小
         lifeSpanSeconds:耗时
         fadeDurationSeconds:渐逝时间
         */
        [self.particleEffect
         addParticleAtPosition:GLKVector3Make(0.0f, 0.0f, 0.9f)
         velocity:GLKVector3Make(randomXVelocity, 1.0f, -1.0f)
         force:GLKVector3Make(0.0f, 9.0f, 0.0f)
         size:8
         lifeSpanSeconds:3
         fadeDurationSeconds:10];
    };

    void(^blockB)(void) = ^{
        self.autoSpawnDelta = 0.05f;

        //重力
        self.particleEffect.gravity = GLKVector3Make(0.0f,3, 0.0f);

        //一次创建多少个粒子
        int n = 3;

        for(int i = 0; i < n; i++)
        {
            //X轴速度
            float randomXVelocity = -0.1f + 0.2f *(float)random() / (float)RAND_MAX;

            //Y轴速度
            float randomZVelocity = 0.1f + 0.2f * (float)random() / (float)RAND_MAX;

            [self.particleEffect
             addParticleAtPosition:GLKVector3Make(0.0f, -0.5f, 0.0f)
             velocity:GLKVector3Make(
                                     randomXVelocity,
                                     -2,
                                     randomZVelocity)
             force:GLKVector3Make(0.0f, 0.0f, 0.0f)
             size:16.0f
             lifeSpanSeconds:3
             fadeDurationSeconds:3.0f];
        }

    };

    void(^blockC)(void) = ^{
        self.autoSpawnDelta = 0.5f;

        //重力
        self.particleEffect.gravity = GLKVector3Make(0.0f, 0.0f, 0.0f);

        int n = 100;
        for(int i = 0; i < n; i++)
        {
            //X,Y,Z速度
            float randomXVelocity = -0.5f + 1.0f * (float)random() / (float)RAND_MAX;
            float randomYVelocity = -0.5f + 1.0f * (float)random() / (float)RAND_MAX;
            float randomZVelocity = -0.5f + 1.0f * (float)random() / (float)RAND_MAX;

            //创建粒子
            [self.particleEffect
             addParticleAtPosition:GLKVector3Make(0.0f, 0.0f, 0.0f)
             velocity:GLKVector3Make(
                                     randomXVelocity,
                                     randomYVelocity,
                                     randomZVelocity)
             force:GLKVector3Make(0.0f, 0.0f, 0.0f)
             size:4.0f
             lifeSpanSeconds:3
             fadeDurationSeconds:1];
        }

    };

    void(^blockD)(void) = ^{
        self.autoSpawnDelta = 3.2f;

        //重力
        self.particleEffect.gravity = GLKVector3Make(0.0f, 0.0f, 0.0f);

        int n = 100;
        for(int i = 0; i < n; i++)
        {
            //X,Y速度
            float randomXVelocity = -0.5f + 1.0f * (float)random() / (float)RAND_MAX;
            float randomYVelocity = -0.5f + 1.0f * (float)random() / (float)RAND_MAX;


            //GLKVector3Normalize 计算法向量
            //计算速度与方向
            GLKVector3 velocity = GLKVector3Normalize( GLKVector3Make(
                                                                     randomXVelocity,
                                                                     randomYVelocity,
                                                                     0.0f));

            [self.particleEffect
             addParticleAtPosition:GLKVector3Make(0.0f, 0.0f, 0.0f)
             velocity:velocity
             force:GLKVector3MultiplyScalar(velocity, -1)
             size:4.0f
             lifeSpanSeconds:3
             fadeDurationSeconds:0.1f];
        }

    };

相关文章

  • GLSL_粒子动画

    1.顶点着色器的构建 1.构建顶点着色器时需要传入的参数 2.顶点着色器main()函数 2.片元着色器的构建 1...

  • 粒子动画效果实现和Swift面向协议开发

    粒子动画效果实现是通过CALayer的子类,CAEmitterLayer实现。 实现粒子动画效果分以下几个步骤: ...

  • 粒子动画

  • 粒子动画

    占位符

  • 粒子动画

    一、在 UIKit 中,粒子系统由两部分组成: 1: 一个或多个 CAEmitterCells :发射器电...

  • 粒子动画

    一个或多个CAEmitterCells:发射器电池可以看作是单个粒子的原型(例如,一个单一的粉扑在一团烟雾)。当散...

  • 粒子动画

    @property(nonatomic, strong) CAEmitterLayer *emitterLayer...

  • 粒子动画

    公司做了一个类似小游戏的项目,背景需要放个流星雨效果粒子动画,详细的不多说,主要自己纪录学习,上代码。 -(voi...

  • 粒子动画

    粒子系统 直播中常用的粒子是怎么搞出来的呢,下面来进行一下探究 CAEmitterLayer 是一个高性能的粒子引...

  • 粒子动画

    什么是粒子系统? 粒子系统是由总体具有相同的表现规律,个体却随机表现出不同的特征的大量显示元素构成的集合 粒子定义...

网友评论

      本文标题:GLSL_粒子动画

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