先上效果圖:
cocos2dx 3D搓牌效果類以下:mvc
-- date:2017/9/26 -- author:looyer -- purpose:3D搓牌效果層, -- 頂點着色器 local strVertSource = [[ attribute vec4 a_position; attribute vec2 a_texCoord; attribute vec4 a_color; uniform float ratio; //牌初始狀態到搓牌最終位置的完成度比例 uniform float radius; //搓牌相似於繞圓柱滾起,其圓柱的半徑 uniform float width; uniform float finish; //是否完成搓牌 uniform float offx; uniform float offy; varying vec4 v_fragmentColor; varying vec2 v_texCoord; void main() { //注意OpenGL-ES中:1.attribute修飾的變量是常量。2.沒有自動轉類型float a = 1;或者5.0/3都是錯誤的 //這兩個問題改了兩天,能夠經過修改CPP代碼,打印log來調試cocos2dx程序 vec4 tmp_pos = a_position; //順時針旋轉90度 tmp_pos = vec4(tmp_pos.y, -tmp_pos.x, tmp_pos.z, tmp_pos.w); if(finish > 0.5) { tmp_pos = vec4(tmp_pos.x, -width - tmp_pos.y, tmp_pos.z, tmp_pos.w); }else { //計算卡牌彎曲的位置,相似於卡牌繞圓柱捲起的原理 float halfPeri = radius * 3.14159; //半周長 float hr = halfPeri * ratio; if(tmp_pos.y < -width + hr) { float dy = -tmp_pos.y - (width - hr); float arc = dy/radius; tmp_pos.y = -width + hr - sin(arc)*radius; tmp_pos.z = radius * (1.0-cos(arc)); //注意以前這裏是1,是錯誤的,opengles不自動類型轉換 } } tmp_pos += vec4(offx, offy, 0.0, 0.0); gl_Position = CC_MVPMatrix * tmp_pos; v_fragmentColor = a_color; v_texCoord = a_texCoord; } ]] -- 片斷着色器 local strFragSource = [[ varying vec4 v_fragmentColor; varying vec2 v_texCoord; void main() { //TODO, 這裏能夠作些片斷着色特效 gl_FragColor = texture2D(CC_Texture0, v_texCoord); } ]] -- 經過圖片取得紋理id,和該紋理在plist圖中的紋理座標範圍 local function getTextureAndRange(szImage) local FrameCache = cc.SpriteFrameCache:getInstance() local sprite = FrameCache:getSpriteFrameByName(szImage) local rect = sprite:getRect() local tex = sprite:getTexture() local id = tex:getName() --紋理ID local bigWide = tex:getPixelsWide() --plist圖集的寬度 local bigHigh = tex:getPixelsHigh() -- 左右上下的紋理範圍 local ll, rr, tt, bb = rect.x/bigWide, (rect.x + rect.width)/bigWide, rect.y/bigHigh, (rect.y + rect.height)/bigHigh return id, {ll, rr, tt, bb}, {rect.width, rect.height} end -- 建立3D牌面,所需的頂點和紋理數據, size:寬高, texRange:紋理範圍, bFront:是否正面 local function initCardVertex(size, texRange, bFront) local nDiv = 200 --將寬分紅100份 local verts = {} --位置座標 local texs = {} --紋理座標 local dh = size.height local dw = size.width/nDiv --計算頂點位置 for c = 1, nDiv do local x, y = (c-1)*dw, 0 local quad = {} if bFront then quad = {x, y, x+dw, y, x, y+dh, x+dw, y, x+dw, y+dh, x, y+dh} else quad = {x, y, x, y+dh, x+dw, y, x+dw, y, x, y+dh, x+dw, y+dh} end for _, v in ipairs(quad) do table.insert(verts, v) end end local bXTex = true --是否當前在計算橫座標紋理座標, for _, v in ipairs(verts) do if bXTex then if bFront then table.insert(texs, v/size.width * (texRange[2] - texRange[1]) + texRange[1]) else table.insert(texs, v/size.width * (texRange[1] - texRange[2]) + texRange[2]) end else if bFront then table.insert(texs, (1-v/size.height) * (texRange[4] - texRange[3]) + texRange[3]) else table.insert(texs, v/size.height * (texRange[3] - texRange[4]) + texRange[4]) end end bXTex = not bXTex end local res = {} local tmp = {verts, texs} for _, v in ipairs(tmp) do local buffid = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, buffid) gl.bufferData(gl.ARRAY_BUFFER, table.getn(v), v, gl.STATIC_DRAW) gl.bindBuffer(gl.ARRAY_BUFFER, 0) table.insert(res, buffid) end return res, #verts end -- 建立搓牌效果層, pList:圖片合集.plist文件, szBack:牌背面圖片名, szFont:牌正面圖片名, 注意:默認傳入的牌在plist文件中是豎直的, scale縮放比 local function createRubCardEffectLayer(pList, szBack, szFont, scale) scale = scale or 1.0 --預加載pList圖集(注重復添加不要緊,只會保存一份) local FrameCache = cc.SpriteFrameCache:getInstance() FrameCache:addSpriteFrames(pList) -- 取得屏幕寬高 local Director = cc.Director:getInstance() local WinSize = Director:getWinSize() -- 建立廣角60度,視口寬高比是屏幕寬高比,近平面1.0,遠平面1000.0,的視景體 local camera = cc.Camera:createPerspective(45, WinSize.width/WinSize.height, 1, 1000) camera:setCameraFlag(cc.CameraFlag.USER2) --設置攝像機的繪製順序,越大的深度越繪製的靠上,因此默認攝像機默認是0,其餘攝像機默認是1, 這句很重要!! camera:setDepth(1) camera:setPosition3D(cc.vec3(0, 0, 800)) camera:lookAt(cc.vec3(0, 0, 0), cc.vec3(0, 1, 0)) -- 建立用於OpenGL繪製的節點 local glNode = gl.glNodeCreate() local glProgram = cc.GLProgram:createWithByteArrays(strVertSource, strFragSource) glProgram:retain() glProgram:updateUniforms() -- 建立搓牌圖層 local layer = cc.Layer:create() layer:setCameraMask(cc.CameraFlag.USER2) layer:addChild(glNode) layer:addChild(camera) -- 退出時,釋放glProgram程序 local function onNodeEvent(event) if "exit" == event then glProgram:release() end end layer:registerScriptHandler(onNodeEvent) local posNow = cc.p(0, 0) --建立觸摸回調 local function touchBegin(touch, event) posNow = touch:getLocation() return true end local function touchMove(touch, event) local location = touch:getLocation() local dy = location.y - posNow.y layer.ratioVal = cc.clampf(layer.ratioVal + dy/100, 0.0, 0.9) --最大程度默認0.9 posNow = location return true end local function touchEnd(touch, event) layer.ratioVal = 0.0 return true end local listener = cc.EventListenerTouchOneByOne:create() listener:registerScriptHandler(touchBegin, cc.Handler.EVENT_TOUCH_BEGAN ) listener:registerScriptHandler(touchMove, cc.Handler.EVENT_TOUCH_MOVED ) listener:registerScriptHandler(touchEnd, cc.Handler.EVENT_TOUCH_ENDED ) local eventDispatcher = layer:getEventDispatcher() eventDispatcher:addEventListenerWithSceneGraphPriority(listener, layer) --建立牌的背面 local id1, texRange1, sz1 = getTextureAndRange(szBack) local msh1, nVerts1 = initCardVertex(cc.size(sz1[1] * scale, sz1[2] * scale), texRange1, true) --建立牌的正面 local id2, texRange2, sz2 = getTextureAndRange(szFont) local msh2, nVerts2 = initCardVertex(cc.size(sz2[1] * scale, sz2[2] * scale), texRange2, false) --搓牌的程度控制, 搓牌相似於經過一個圓柱滾動將牌粘着起來的效果。下面的參數就是滾動程度和圓柱半徑 layer.ratioVal = 0.0 layer.radiusVal = sz1[1]*scale/math.pi; --牌的渲染信息 local cardMesh = {{id1, msh1, nVerts1}, {id2, msh2, nVerts2}} -- OpenGL繪製函數 local function draw(transform, transformUpdated) gl.enable(gl.CULL_FACE) glProgram:use() glProgram:setUniformsForBuiltins() for _, v in ipairs(cardMesh) do gl.bindTexture(gl.TEXTURE_2D, v[1]) -- 傳入搓牌程度到着色器中,進行位置計算 local ratio = gl.getUniformLocation(glProgram:getProgram(), "ratio") glProgram:setUniformLocationF32(ratio, layer.ratioVal) local radius = gl.getUniformLocation(glProgram:getProgram(), "radius") glProgram:setUniformLocationF32(radius, layer.radiusVal) -- 偏移牌,使得居中 local offx = gl.getUniformLocation(glProgram:getProgram(), "offx") glProgram:setUniformLocationF32(offx, WinSize.width/2 - sz1[2]/2*scale) local offy = gl.getUniformLocation(glProgram:getProgram(), "offy") glProgram:setUniformLocationF32(offy, WinSize.height/2 + sz1[1]/2*scale) local width = gl.getUniformLocation(glProgram:getProgram(), "width") glProgram:setUniformLocationF32(width, sz1[1]*scale) gl.glEnableVertexAttribs(bit._or(cc.VERTEX_ATTRIB_FLAG_TEX_COORDS, cc.VERTEX_ATTRIB_FLAG_POSITION)) gl.bindBuffer(gl.ARRAY_BUFFER, v[2][1]) gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION,2,gl.FLOAT,false,0,0) gl.bindBuffer(gl.ARRAY_BUFFER, v[2][2]) gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORD,2,gl.FLOAT,false,0,0) gl.drawArrays(gl.TRIANGLES, 0, v[3]) end gl.bindTexture(gl.TEXTURE_2D, 0) gl.bindBuffer(gl.ARRAY_BUFFER, 0) end glNode:registerScriptDrawHandler(draw) return layer end return createRubCardEffectLayer
演示類以下:app
-- date:2017/09/22 -- author:looyer -- purpose:效果測試 local RubCardFunc = require("app.views.RubCardLayer") local EffectTestNode = class("EffectTestNode", cc.load("mvc").ViewBase) function EffectTestNode:onCreate() end function EffectTestNode:onEnter() --顯示搓牌效果 local layer = RubCardFunc("res/UI/images/card_cor.plist", "pkc_paimian.png", "pkc1_1.png", 2.0) self:addChild(layer) end function EffectTestNode:onClickClose() self:getApp():removeView(self:getName()) end return EffectTestNode