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