想法:使用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__
#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>
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; }
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; }
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; }
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