cocos2d-x / android 探照灯特效(SpotLight)

34 篇文章 0 订阅
2 篇文章 0 订阅

想法:使用openGL画圆,顶点为圆心+圆周上的点,顶点颜色值(0, 0, 0, 0),圆周点上的颜色值(0, 0, 0, 0xFF),开启Alpha混合,使用glBlendFunc(GL_ZERO, GL_SRC_ALPHA);方式,中间区域openGL会自动渐变。


效果:



代码:

Cococs2d-x实现

SpotLight.h

#ifndef __SPOT_LIGHT_H__
#define __SPOT_LIGHT_H__

class CCSpotLight: public CCSprite
{
public:
	CCSpotLight();
	~CCSpotLight();

	static CCSpotLight* spotLightWithRenderTexture(CCRenderTexture* texture, float radius, ccColor4B color);


	CC_SYNTHESIZE_RETAIN(CCRenderTexture*, m_renderTexture, RenderTexture)
	CC_SYNTHESIZE(float, m_spotLightRadius, SpotLightRadius)
	CC_SYNTHESIZE_PASS_BY_REF(ccColor4B, m_renderColor, RenderColor)
private:
	bool initWithRenderTexture(CCRenderTexture* texture, float radius, ccColor4B color);
	void draw();
};

#endif// __SPOT_LIGHT_H__

SpotLight.cpp

#include <cocos2d.h>
using namespace cocos2d;

#include "SpotLight.h"

const int SPOT_LIGHT_VERTICES_COUNT = 45;// 必须大于5, 圆心和圆周上的顶点数
const ccColor4B SPOT_LIGHT_CENTER_COLOR = ccc4(0, 0, 0, 0);
const ccColor4B SPOT_LIGHT_EDGE_COLOR = ccc4(0, 0, 0, 0xFF);


CCSpotLight::CCSpotLight()
{
	m_renderTexture = NULL;
	m_spotLightRadius = 0;
	m_renderColor.r = 0;
	m_renderColor.g = 0;
	m_renderColor.b = 0;
	m_renderColor.a = 0;
}


CCSpotLight::~CCSpotLight()
{
	CC_SAFE_RELEASE(m_renderTexture);
}


CCSpotLight* CCSpotLight::spotLightWithRenderTexture(CCRenderTexture* texture, float radius, ccColor4B color)
{
	CCSpotLight* spotLight = NULL;

	spotLight = new CCSpotLight();
	if (spotLight && spotLight->initWithRenderTexture(texture, radius, color))
	{
		spotLight->autorelease();
		return spotLight;
	}

	CC_SAFE_DELETE(spotLight);
	return spotLight;
}


bool CCSpotLight::initWithRenderTexture(CCRenderTexture* texture, float radius, ccColor4B color)
{
	bool bRet = false;

	do
	{
		bRet = CCSprite::init();
		CC_BREAK_IF(!bRet);

		setRenderTexture(texture);
		setSpotLightRadius(radius);
		setRenderColor(color);

		bRet = true;
	}while(0);

	return bRet;
}


void CCSpotLight::draw()
{
	CCSprite::draw();

	int segs = SPOT_LIGHT_VERTICES_COUNT;
	GLfloat *vertices = new GLfloat[2*segs];//malloc( sizeof(GLfloat)*2*(segs));
	GLfloat *coordinates = new GLfloat[2*segs];malloc( sizeof(GLfloat)*2*(segs));
	ccColor4B *colors = new ccColor4B[segs];//malloc( sizeof(ccColor4B)*(segs));
	
	memset(vertices,0, sizeof(GLfloat)*2*(segs));
	memset(coordinates,0, sizeof(GLfloat)*2*(segs));
	
	CCSize winSize = CCDirector::sharedDirector()->getWinSize();

	m_renderTexture->clear(m_renderColor.r, m_renderColor.g, m_renderColor.b, m_renderColor.a);
	//m_renderTexture->clear(0, 0, 0, 0xFF);
	
	colors[0] = SPOT_LIGHT_CENTER_COLOR;
	for (int i = 1; i < segs; i++)
	{
		colors[i] = SPOT_LIGHT_EDGE_COLOR;
	}
	
	const float coef = 2.0f * (float)M_PI/(segs-2) ;
	CCPoint pos = getPosition();
	CCSize size = this->getContentSize();

	vertices[0] = 0;//pos.x;
	vertices[1] = 0;//pos.y;
	coordinates[0] = vertices[0]/winSize.width;
	coordinates[1] = (size.height - vertices[1])/winSize.height;
	for(int i=1;i<segs;i++)
	{
		float rads = i*coef;
		float j = m_spotLightRadius * cosf(rads);// + pos.x;
		float k = m_spotLightRadius * sinf(rads);// + pos.y;
		
		vertices[i*2] = j;
		vertices[i*2+1] = k;
		
		coordinates[i*2] = (j)/winSize.width;
		coordinates[i*2+1] = (size.height-k)/winSize.height;
	}
	
    // Update the render texture
    //[self.renderTexture begin];
	m_renderTexture->begin();
	
    glBindTexture(GL_TEXTURE_2D, (GLuint)m_renderTexture);
	glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
	
	glVertexPointer(2, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
	glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
	
	glDrawArrays(GL_TRIANGLE_FAN, 0, segs);
	
	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
	
    //[self.renderTexture end];
	m_renderTexture->end();
	
	CC_SAFE_DELETE(vertices);
	CC_SAFE_DELETE(coordinates);
	CC_SAFE_DELETE(colors);
}
调用方式:

		CCRenderTexture* renderLayer = CCRenderTexture::renderTextureWithWidthAndHeight(size.width, size.height);
		renderLayer->setPosition(ccp(size.width/2, size.height/2));
		ccBlendFunc bf = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA};
		renderLayer->getSprite()->setBlendFunc(bf);
		addChild(renderLayer);
		
		CCSpotLight* spotLight = CCSpotLight::spotLightWithRenderTexture(renderLayer, 60.0f, ccc4(0, 0, 0, 0xFF));
		CC_BREAK_IF(!spotLight);
		spotLight->setAnchorPoint(ccp(0, 0));
		spotLight->setPosition(ccp(size.width/2, size.height/2));
		//spotLight->setPosition(ccp(0, 0));
		addChild(spotLight);

参考:

1.ios版聚光灯效果演示程序http://www.supersuraccoon-cocos2d.com/zh/2011/09/09/spot-light-demo/


===================================================================================


Android

main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ImageView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:src="@drawable/dota" 
        />
    
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:textColor="@android:color/primary_text_light"
        android:text="@string/hello" />
    
    <Button
        android:id="@+id/btn_click_me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btn_click" 
        />
    <com.kle.SpotLight.SpotLightGLSurfaceView
        android:id="@+id/spotlight_gl_surfaceview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

</FrameLayout>

SpotLightActivity.java

package com.gr.SpotLight;

import android.app.Activity;
import android.os.Bundle;

public class SpotLightActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        m_glView = (SpotLightGLSurfaceView)findViewById(R.id.spotlight_gl_surfaceview);
        
        SpotLightRender renderer = new SpotLightRender(480, 800);
        m_glView.setRenderer(renderer);
        
        SpotLight sLight = new SpotLight(0, 480, 100, 5);// 坐标系为GL坐标系,即屏幕左下角为坐标原点,向右为x轴正方向,向上为y轴正方向
        m_glView.addSpotLight(sLight);
        sLight = new SpotLight(0, 480, 400, 10);// 坐标系为GL坐标系,即屏幕左下角为坐标原点,向右为x轴正方向,向上为y轴正方向
        m_glView.addSpotLight(sLight);
        
        //sLight.setPosition(240, 400);//可以自己设置坐标
        
        Button btn = (Button)findViewById(R.id.btn_click_me);
        btn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v)
            {
                // TODO Auto-generated method stub
                Toast.makeText(getApplicationContext(), "Click Me!", Toast.LENGTH_SHORT).show();
            }
            
        });
    }
    SpotLightGLSurfaceView m_glView = null;
}

SpotLightGLSurfaceView.java

package com.gr.SpotLight;

import android.content.Context;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.view.SurfaceHolder;

public class SpotLightGLSurfaceView extends GLSurfaceView
{

    public SpotLightGLSurfaceView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        

        // 这部分代码必须在 setRenderer 前
        setZOrderOnTop(true);
        setEGLConfigChooser(8, 8, 8, 8, 16, 0);

        SurfaceHolder holder = getHolder();
        holder.setFormat(PixelFormat.TRANSLUCENT);
    }
    
    public void setRenderer(Renderer renderer)
    {
        super.setRenderer(renderer);
        m_renderer = (SpotLightRender)renderer;
    }
    
    public int addSpotLight(SpotLight sl)
    {
        int nRet = 0;
        
        if (m_renderer != null)
        {
            m_renderer.addSpotLight(sl);
        }
        
        return nRet;
    }
    
    public void updateSpotLights()
    {
        if (m_renderer != null)
        {
            m_renderer.updateMove();
        }
    }

    private SpotLightRender m_renderer= null;
}

SpotLightRender.java

package com.gr.SpotLight;

import java.util.ArrayList;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView.Renderer;

public class SpotLightRender implements Renderer
{

    public SpotLightRender(int width, int height)
    {
        super();
        m_width = width;
        m_height = height;
    }
    
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        // TODO Auto-generated method stub

        gl.glDisable(GL10.GL_DITHER);
        
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
        
        gl.glClearColor(0, 0, 0, 1);
        
        gl.glShadeModel(GL10.GL_SMOOTH);
        //gl.glEnable(GL10.GL_CULL_FACE);
        gl.glOrthof(0, m_width, 0, m_height, 1, -1);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        // TODO Auto-generated method stub
        m_width = width;
        m_height = height;

        gl.glViewport(0, 0, width, height);
        // make adjustments for screen ratio
        float ratio = (float)width / height;
        gl.glMatrixMode(GL10.GL_PROJECTION);// set matrix to projection mode
        gl.glLoadIdentity();// reset the matrix to its default state
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);// apply the projection matrix
    }

    @Override
    public void onDrawFrame(GL10 gl)
    {
        // TODO Auto-generated method stub

        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
        
        for (int i = 0; i < m_spotLightArray.size(); i++)
        {
            ((SpotLight)m_spotLightArray.get(i)).moveByFrame();
            ((SpotLight)m_spotLightArray.get(i)).draw(gl);
        }
    }
    
    public int addSpotLight(SpotLight spotLight)
    {
        int nRet = 0;
        
        if (m_spotLightArray == null)
        {
            m_spotLightArray = new ArrayList<SpotLight>();
        }
        
        if (m_spotLightArray != null && spotLight != null)
        {
            m_spotLightArray.add(spotLight);
            nRet = m_spotLightArray.size();
        }
        
        return nRet;
    }
    
    public void clearSpotLightArray()
    {
        if (m_spotLightArray != null)
        {
            m_spotLightArray.clear();
            m_spotLightArray = null;
        }
    }
    
    public void updateMove()
    {
        for (int i = 0; i < m_spotLightArray.size(); i++)
        {
            ((SpotLight)m_spotLightArray.get(i)).moveByFrame();
        }
    }

    private ArrayList<SpotLight> m_spotLightArray = new ArrayList<SpotLight>();
    private int m_width = 480;
    private int m_height = 800;
}

SpotLight.java

package com.gr.SpotLight;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PointF;

public class SpotLight
{
    public SpotLight()
    {
        init(0, 0, 0, VELOCITY_DEFAULT, RADIUS_DEFAULT, VERTEX_COUNT_DEFAULT);
    }
    
    public SpotLight(float lx, float rx, float y, float v)
    {
        init(lx, rx, y, v, RADIUS_DEFAULT, VERTEX_COUNT_DEFAULT);
    }
    
    public SpotLight(float lx, float rx, float y, float v, float radius)
    {
        init(lx, rx, y, v, radius, VERTEX_COUNT_DEFAULT);
    }
    
    public SpotLight(float lx, float rx, float y, float v, float radius, int vertexCount)
    {
        init(lx, rx, y, v, radius, vertexCount);
    }
    
    private void init(float lx, float rx, float y, float v, float radius, int vertexCount)
    {
        if (lx > rx)
        {
            float tmp = rx;
            rx = lx;
            lx = tmp;
        }
        m_leftX = lx;
        m_rightX = rx;
        m_myDirector = m_rand.nextBoolean()?DIRECTOR_LEFT:DIRECTOR_RIGHT;
        float x = m_rand.nextFloat() * (m_rightX - m_leftX) + m_leftX;
        m_position.set(x, y);
        m_vByFrame = v;
        m_radius = radius;
        m_vertexCount = vertexCount;
        
        initVertices();
    }
    
    private void initVertices()
    {
        int i = 0;
        
        m_vertices = new float[POINT_SIZE * m_vertexCount];
        m_colors = new int[COLOR_SIZE * m_vertexCount];
        
        double coef = 2.0 * Math.PI / (m_vertexCount - 2);
        // 圆心
        m_vertices[0] = 0;//m_position.x;
        m_vertices[1] = 0;//m_position.y;
        m_colors[0] = 0;
        m_colors[1] = 0;
        m_colors[2] = 0;
        m_colors[3] = 0;
        
        for (i = 1; i < m_vertexCount; i++)
        {
            double rads = i * coef;
            
            m_vertices[i * POINT_SIZE] = (float)(m_vertices[0] + m_radius * Math.cos(rads));
            m_vertices[i * POINT_SIZE + 1] = (float)(m_vertices[1] + m_radius * Math.sin(rads));
                        
            m_colors[i * COLOR_SIZE] = 0;//0xFFFF;
            m_colors[i * COLOR_SIZE + 1] = 0;
            m_colors[i * COLOR_SIZE + 2] = 0;
            m_colors[i * COLOR_SIZE + 3] = 0xFFFF;//0;
        }
        
        convert2GLpos();

        ByteBuffer cbb = ByteBuffer.allocateDirect(m_colors.length * Integer.SIZE / Byte.SIZE);
        cbb.order(ByteOrder.nativeOrder());
        m_ColorBuffer = cbb.asIntBuffer();
        m_ColorBuffer.put(m_colors);
        m_ColorBuffer.position(0);
    }
    
    private void convert2GLpos()
    {
        float vt[] = new float[POINT_SIZE * m_vertexCount];
        int i = 0;
        for (i = 0; i < m_vertexCount; i++)
        {
            vt[i * POINT_SIZE] = m_vertices[i * POINT_SIZE] + m_position.x;
            vt[i * POINT_SIZE + 1] = m_vertices[i * POINT_SIZE + 1] + m_position.y;
        }
        
        ByteBuffer vbb = ByteBuffer.allocateDirect(vt.length * Float.SIZE / Byte.SIZE);
        vbb.order(ByteOrder.nativeOrder());
        m_VertexBuffer = vbb.asFloatBuffer();
        m_VertexBuffer.put(vt);
        m_VertexBuffer.position(0);
    }
    
    public void draw(GL10 gl)
    {
        gl.glFrontFace(GL10.GL_CW);

        gl.glVertexPointer(POINT_SIZE, GL10.GL_FLOAT, 0, m_VertexBuffer);
        gl.glColorPointer(COLOR_SIZE, GL10.GL_FIXED, 0, m_ColorBuffer);
        
        gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, m_vertexCount);
    }
    
    public void moveByFrame()
    {
        m_position.offset(m_myDirector * m_vByFrame, 0);
        if (m_position.x > m_rightX)
        {
            m_position.x = m_rightX;
            m_myDirector = DIRECTOR_LEFT;
        }
        else if (m_position.x < m_leftX)
        {
            m_position.x = m_leftX;
            m_myDirector = DIRECTOR_RIGHT;
        }
        convert2GLpos();
    }
    
    public void setPosition(float x, float y)
    {
        m_position.set(x, y);
        convert2GLpos();
    }
    
    public void offPosition(float dx, float dy)
    {
        m_position.offset(dx, dy);
        convert2GLpos();
    }
    
    public void setRadius(float r)
    {
        m_radius = r;

        double coef = 2.0 * Math.PI / (m_vertexCount - 2);
        int i = 0;
        for (i = 1; i < m_vertexCount; i++)
        {
            double rads = i * coef;
            
            m_vertices[i * POINT_SIZE] = (float)(m_vertices[0] + m_radius * Math.cos(rads));
            m_vertices[i * POINT_SIZE + 1] = (float)(m_vertices[1] + m_radius * Math.sin(rads));
        }
        convert2GLpos();
    }
    
    private PointF m_position = new PointF();// 相对坐标,在gl_surfaceview内的坐标
    private float m_leftX = 0;
    private float m_rightX = 0;
    private float m_radius;
    private int m_vertexCount;
    private Point m_parentWinSize = new Point();
    
    private float m_vertices[] = null;
    private int m_colors[] = null;
    
    private FloatBuffer m_VertexBuffer = null;
    private IntBuffer m_ColorBuffer = null;
    
    private Random m_rand = new Random(System.currentTimeMillis());
    private final static int DIRECTOR_LEFT = -1;
    private final static int DIRECTOR_RIGHT = 1;
    private int m_myDirector = DIRECTOR_LEFT;
    private final static float VELOCITY_DEFAULT = 1;
    private float m_vByFrame = VELOCITY_DEFAULT;
    
    private final static float RADIUS_DEFAULT = 100;
    private final static int VERTEX_COUNT_DEFAULT = 45;// > 5
    private final static int POINT_SIZE = 2;
    private final static int COLOR_SIZE = 4;// r, g, b, a
    private final static int CENTER_COLOR = Color.argb(0x00, 0x00, 0x00, 0x00);
    private final static int EDGE_COLOR = Color.argb(0xFF, 0x00, 0x00, 0x00);
    
}

参考:

1.android3D物体的碰撞——正方体的碰撞http://www.2cto.com/kf/201110/109245.html

2.使GLSurfaceview透明 可见背景图片 http://blog.csdn.net/cc_lq/article/details/6629659

http://blog.sina.com.cn/s/blog_7705f5140100rive.html

http://topic.csdn.net/u/20110215/16/c2849359-b07e-424f-bf0d-db0506b69002.html


总结:

1.基本不会openGL,所以弄起来都是一知半解,头好大,需要好好学学openGL了。

2.一开始我是仿照cocos2d-x的代码写的Android代码,狗屁不通呀,就是显示不出来,还各种崩。

3.glSurfaceView透明参考的是使GLSurfaceview透明 可见背景图片:主要3句话

mGLSurfaceView.setZOrderOnTop(true);

mGLSurfaceView.setEGLConfigChooser(8,8,8,8,16,0);

mGLSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);

mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);要在setRender之前加上,有木有啊有木有,坑死爹了

4.color的问题:

glClearColor参数都是float呀,就是0~1之间的数,r g b a 的顺序

glColorPointer


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GrimRaider

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值