想法:使用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