Ai
1 Star 1 Fork 2

小康6650/Cocos2dGraphicEffect

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
ShadowScene.cpp 15.64 KB
一键复制 编辑 原始数据 按行查看 历史
小康6650 提交于 2019-02-17 22:53 +08:00 . finish Shadow.
#include "ShadowScene.h"
#include "../3DModel.h"
#pragma warning(disable:4996)
USING_NS_CC;
RenderBufferDepthTexture::RenderBufferDepthTexture()
{
_type = Type::Custom; //这个类型也是我增加的,当为该类型是,设置帧缓存附件时会调用onApplyFBO函数
_tex = 0;
}
RenderBufferDepthTexture::~RenderBufferDepthTexture()
{
}
RenderBufferDepthTexture * RenderBufferDepthTexture::create(unsigned int size)
{
auto result = new (std::nothrow) RenderBufferDepthTexture();
if (result && result->init(size))
{
result->autorelease();
return result;
}
else
{
CC_SAFE_DELETE(result);
return nullptr;
}
}
bool RenderBufferDepthTexture::init(unsigned int size)
{
if (!RenderTargetBase::init(size, size))
{
return false;
}
glGenTextures(1, &_tex);
glBindTexture(GL_TEXTURE_2D, _tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, size, size, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//设置比较深度纹理函数,设置后只能通过sampler2DShadow比较深度,不可用sampler2D读取深度值
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
//为超出光照视角外的位置设置深度参考值。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
void RenderBufferDepthTexture::onApplyFBO(GLenum target)
{
//为了实现设置深度纹理稍微修改了以下引擎,修改的详细信息请看文件\cocos2d\cocos\renderer\CCFrameBuffer.cpp 469行
glFramebufferTexture(target, GL_DEPTH_ATTACHMENT, _tex, 0);
glDrawBuffer(GL_NONE);//屏蔽颜色绘制,不需要绘制颜色,只是要获取深度缓存
}
//////////////////////////////////////////////////////////////////////////
ShadowCamera::ShadowCamera()
{
_rt = nullptr;
}
ShadowCamera::~ShadowCamera()
{
CC_SAFE_RELEASE(_rt);
}
ShadowCamera * ShadowCamera::create(cocos2d::Camera *viewCamera, cocos2d::CameraFlag shadowFlag)
{
ShadowCamera* ret = new(std::nothrow) ShadowCamera();
if (ret && ret->init(viewCamera, shadowFlag))
{
ret->autorelease();
return ret;
}
else
{
delete ret;
return nullptr;
}
}
bool ShadowCamera::init(cocos2d::Camera *viewCamera, cocos2d::CameraFlag shadowFlag)
{
this->setCameraFlag(shadowFlag);
this->setDepth(viewCamera->getDepth() - 1); //shadow camera 先于 view Camera 绘制
const unsigned int Size = 1024;
using namespace experimental;
FrameBuffer *fb = FrameBuffer::create(1, Size, Size);
_rt = RenderBufferDepthTexture::create(Size);//将深度信息绘制在纹理中
_rt->retain();
fb->attachRenderTarget(_rt);
this->setFrameBufferObject(fb);
return true;
}
GLuint ShadowCamera::GetShadowTexture()
{
if (nullptr != _rt)
{
return _rt->getShadowTextureName();
}
return 0;
}
void ShadowCamera::setPorjectionMatrix(cocos2d::Mat4 &projection)
{
_projection = projection;
getViewProjectionMatrix();
}
//////////////////////////////////////////////////////////////////////////
//Cocos2d引擎的提供的Shader不支持阴影映射,需要修改,修改后会生成相应的Material
Sprite3DMaterial * ShadowSprite::_shadowDiffuseMaterial = nullptr; //有光照,无纹理,无骨骼
Sprite3DMaterial * ShadowSprite::_shadowDiffuseNoTexMaterial = nullptr; //有光照,有纹理,有骨骼
Material * ShadowSprite::getShadowMaterial(Sprite3DMaterial::MaterialType type)
{
//每种Material需要两种特效,也就需要两个Technique,
//一个用于正常观察视角下绘制图像,一个用于光源视角下绘制深度信息
if (Sprite3DMaterial::MaterialType::DIFFUSE == type)//有光照,有纹理,有骨骼
{
if (nullptr == _shadowDiffuseMaterial)
{
auto program = CCGLProgram::createWithFilenames("ccShader_3D_PositionNormalTex.vert", "ccShader_3D_ColorNormalTex.frag");
auto programstate = GLProgramState::create(program);
auto diffuseSkinTexMaterial = Sprite3DMaterial::createWithGLStateProgram(programstate);//默认会创建一个Technique,位于getTechniqueByIndex(0),用于正常观察视角下绘制图像
program = CCGLProgram::createWithFilenames("ccShader_3D_PositionNormalTex.vert", "ShaderNoColor.frag");//用于生成深度纹理
programstate = GLProgramState::create(program);
auto depthTechnique = Technique::createWithGLProgramState(diffuseSkinTexMaterial, programstate);
diffuseSkinTexMaterial->addTechnique(depthTechnique);//增加一个深度绘制使用的Technique, 位于getTechniqueByIndex(1)
diffuseSkinTexMaterial->retain();
_shadowDiffuseMaterial = diffuseSkinTexMaterial;
}
return _shadowDiffuseMaterial->clone();
}
else if (Sprite3DMaterial::MaterialType::DIFFUSE_NOTEX == type)//有光照,无纹理,无骨骼
{
if (nullptr == _shadowDiffuseNoTexMaterial)
{
auto program = CCGLProgram::createWithFilenames("ccShader_3D_PositionNormal.vert", "ccShader_3D_ColorNormal.frag");
auto programstate = GLProgramState::create(program);
auto diffuseNoTexMaterial = Sprite3DMaterial::createWithGLStateProgram(programstate);//默认会创建一个Technique,用于正常观察视角下绘制图像
program = CCGLProgram::createWithFilenames("ccShader_3D_PositionNormal.vert", "ShaderNoColor.frag");//用于生成深度纹理
programstate = GLProgramState::create(program);
auto depthTechnique = Technique::createWithGLProgramState(diffuseNoTexMaterial, programstate);
diffuseNoTexMaterial->addTechnique(depthTechnique);
diffuseNoTexMaterial->retain();
_shadowDiffuseNoTexMaterial = diffuseNoTexMaterial;
}
return _shadowDiffuseNoTexMaterial->clone();
}
return nullptr;
}
ShadowSprite::ShadowSprite() : _shadowCamera(nullptr)
{
}
ShadowSprite::~ShadowSprite()
{
CC_SAFE_RELEASE(_shadowCamera);
}
ShadowSprite * ShadowSprite::create()
{
auto sprite = new (std::nothrow) ShadowSprite();
if (sprite && sprite->init())
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return nullptr;
}
ShadowSprite * ShadowSprite::create(const std::string & modelPath)
{
CCASSERT(modelPath.length() >= 4, "invalid filename for ShadowSprite");
auto sprite = new (std::nothrow) ShadowSprite();//创建ShadowSprite对象
if (sprite && sprite->initWithFile(modelPath))
{
sprite->_contentSize = sprite->getBoundingBox().size;
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return nullptr;
}
ShadowSprite * ShadowSprite::create(const std::string & modelPath, const std::string & texturePath)
{
auto sprite = create(modelPath);
if (sprite)
{
sprite->setTexture(texturePath);
}
return sprite;
}
bool ShadowSprite::initWithFile(const std::string &path)
{
bool ret = Sprite3D::initWithFile(path);
if (ret)
{
//根据模型设置创建的Material,以支持阴影生成和绘制
for (int i = 0; i < _meshes.size(); ++i)
{
auto mesh = _meshes.at(i);
bool textured = mesh->getMeshIndexData()->getMeshVertexData()->hasVertexAttrib(GLProgram::VERTEX_ATTRIB_TEX_COORD);
Sprite3DMaterial::MaterialType type;
if (textured)
{
type = Sprite3DMaterial::MaterialType::DIFFUSE;
}
else
{
type = Sprite3DMaterial::MaterialType::DIFFUSE_NOTEX;
}
auto material = getShadowMaterial(type);
setMaterial(material, i);
material->getStateBlock()->setDepthTest(true);//开启深度比较测试
material->getStateBlock()->setDepthWrite(true);
material->getStateBlock()->setCullFace(false);//禁用背面裁剪
}
}
return ret;
}
void ShadowSprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
if (nullptr != _shadowCamera)
{
//根据摄像机选择使用哪个Technique,Technique[0]是绘制正常图像,Technique[1]是生成深度纹理
if (Camera::getVisitingCamera() == _shadowCamera)//正在生成深度纹理
{
for (int i = 0; i < _meshes.size(); ++i)
{
auto mesh = _meshes.at(i);
//这个函数是我修改了引擎增加的
//如果不想修改引擎可以使用配置文件名创建Material, 文件中可以指的Technique的名字,
//从而可以使用setTechnique(const std::string& techniqueName)函数。
mesh->getMaterial()->setTechniqueByIndex(1); //生成深度纹理
}
}
else
{
Mat4 t, s, ScalBias;
Mat4::createTranslation(0.5f, 0.5f, 0.5f, &t);
Mat4::createScale(0.5, 0.5, 0.5, &s);
ScalBias = t * s;//OpenGL显示时使用的是NDC坐标,x,y,z的值都在[-1, 1]之间,存储到深度纹理时x,y会被压缩到[0.0,1.0]的坐标区间,深度值Z也会被压缩到[0.0, 1.0]之间,
//所以需要对x,y,z进行压缩处理,矩阵ScalBias的作用就是将x,y,z从[-1, 1]压缩调整到[0.0,1.0]
for (int i = 0; i < _meshes.size(); ++i)
{
auto mesh = _meshes.at(i);
mesh->getMaterial()->setTechniqueByIndex(0);
auto ps = getMaterial(i)->getTechniqueByIndex(0)->getPassByIndex(0)->getGLProgramState();
//深度纹理使用的采样器为sampler2DShadow,Cocos2d原生不支持这种采样器,需要稍微修改以下引擎,
//详见文件\cocos2d\cocos\renderer\CCGLProgramState.cpp第119行 GL_SAMPLER_2D_SHADOW
ps->setUniformTexture("DepthTex", _shadowCamera->GetShadowTexture());
//先在光线视角下投影,然后用矩阵进行压缩调整到[0,1]
Mat4 ligthPV = ScalBias * _shadowCamera->getViewProjectionMatrix();
ps->setUniformMat4("LightScalePVMatrix", ligthPV);
}
}
}
Sprite3D::draw(renderer, transform, flags);
}
void ShadowSprite::setCameraMask(unsigned short mask, bool applyChildren /* = true */)
{
if (nullptr != _shadowCamera)
{
mask |= (unsigned short)_shadowCamera->getCameraFlag();
}
Sprite3D::setCameraMask(mask, applyChildren);
}
void ShadowSprite::setShadowCamera(ShadowCamera * camera, bool applyChildren)
{
CC_SAFE_RELEASE(_shadowCamera);
_shadowCamera = camera;
CC_SAFE_RETAIN(_shadowCamera);
Sprite3D::setCameraMask(getCameraMask() | (unsigned short)camera->getCameraFlag(), applyChildren);
}
//////////////////////////////////////////////////////////////////////////
Scene* ShadowScene::createScene()
{
return ShadowScene::create();
}
// on "init" you need to initialize your instance
bool ShadowScene::init()
{
//////////////////////////////
// 1. super init first
if ( !Scene::init() )
{
return false;
}
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
auto closeItem = MenuItemFont::create("Back", CC_CALLBACK_1(ShadowScene::menuBackCallback, this));
float x = origin.x + visibleSize.width - closeItem->getContentSize().width/2;
float y = origin.y + closeItem->getContentSize().height/2;
closeItem->setPosition(Vec2(x,y));
// create menu, it's an autorelease object
auto menu = Menu::create(closeItem, NULL);
menu->setPosition(Vec2::ZERO);
this->addChild(menu, 1);
/////////////////////////////
// 3. add your codes below...
auto camera = Camera::create();
camera->initPerspective(60, visibleSize.width/visibleSize.height, 5, 5000);
camera->setPosition3D(Vec3(0.f, _aspect, _cameraRadius));
camera->lookAt(Vec3(0.0f, 0.0f, 0.0f), Vec3(0.0f, 1.0f, 0.f));
camera->setCameraFlag(CameraFlag::USER1);//View Camera
camera->setDepth(-10);
this->addChild(camera);
//////////////////////////////////////////////////////////////////////////
//设置光源视角摄像机
auto shadowCamera = ShadowCamera::create(camera, CameraFlag::USER2);
#define USE_POINT_LIGHT_SHADOW
#ifdef USE_POINT_LIGHT_SHADOW
Vec3 LightPos(500, 800, 200);
shadowCamera->initPerspective(120, 1, 50, 5000);//点光源使用透视投影
#else
Vec3 LightPos(1000, 1000, 000);
Mat4 projection;
Mat4::createOrthographicOffCenter(-1000, 1000, -1000, 1000, 50, 5000, &projection);
shadowCamera->setPorjectionMatrix(projection);//平行光要使用正交投影
#endif // USE_POINT_LIGHT_SHADOW
shadowCamera->setPosition3D(LightPos);
shadowCamera->lookAt(Vec3(0.0f, 0.0f, 0.0f), Vec3(0.0f, 1.0f, 0.f));
this->addChild(shadowCamera);
auto alight = AmbientLight::create(Color3B(100, 100, 100));
auto dlight = PointLight::create(LightPos, Color3B(140, 140, 140), 5000);
this->addChild(alight);
this->addChild(dlight);
//绘制一个白球显示点光源大概的位置
auto pointLight = Sprite3D::create("3DModel\\Ball.c3b");
pointLight->setColor(Color3B(255, 255, 255));
pointLight->setScale(0.4);
pointLight->setPosition3D(LightPos + Vec3(40, 40, -40));
this->addChild(pointLight);
pointLight->setCameraMask((unsigned short)CameraFlag::USER1);
auto ground = ShadowSprite::create("3DModel\\Panel.c3t");
ground->setScale(10);
ground->setColor(Color3B(50, 150, 180));
ground->setRotation3D(Vec3(-90, 0, 0));
this->addChild(ground);
ground->setCameraMask((unsigned short)CameraFlag::USER1);
ground->setShadowCamera(shadowCamera);
auto Ball = ShadowSprite::create("3DModel\\Ball.c3b");
Ball->setColor(Color3B(0, 255, 255));
Ball->setPosition3D(Vec3(350, 520, -60));
this->addChild(Ball);
Ball->setScale(0.5);
Ball->setCameraMask((unsigned short)CameraFlag::USER1);
Ball->setShadowCamera(shadowCamera);
auto orc = ShadowSprite::create("3DModel\\orc.c3b");
orc->setPosition3D(Vec3(-200, 20, 300));
orc->setScale(20);
orc->setRotation3D(Vec3(0, 180, 0));
this->addChild(orc);
orc->setCameraMask((unsigned short)CameraFlag::USER1);
orc->setShadowCamera(shadowCamera);
auto animation = Animation3D::create("3DModel\\orc.c3b");
orc->runAction(RepeatForever::create(Animate3D::create(animation)));
auto cube = ShadowSprite::create("3DModel\\cube.c3b");
cube->setPosition3D(Vec3(150, 220, -300));
cube->setColor(Color3B(185, 135, 55));
this->addChild(cube);
cube->setCameraMask((unsigned short)CameraFlag::USER1);
cube->setShadowCamera(shadowCamera);
auto rotate_action = RotateBy::create(1.5, Vec3(0, 30, 0));
cube->runAction(RepeatForever::create(rotate_action));
_mouseListen = EventListenerMouse::create();
_mouseListen->onMouseDown = [this](EventMouse *e) {
if (e->getMouseButton() == EventMouse::MouseButton::BUTTON_LEFT)
{
_isLeftButtonDown = true;
}
_mouseLatestPosition = Vec2(e->getCursorX(), e->getCursorY());
};
_mouseListen->onMouseMove = [camera, this](EventMouse *e) {
auto mousePosition = Vec2(e->getCursorX(), e->getCursorY());
auto delta = mousePosition - _mouseLatestPosition;
if (_isLeftButtonDown)
{
_angle += CC_DEGREES_TO_RADIANS(delta.x);
if (_isCrtlKeyDown)
{
float newRadius = _cameraRadius - 6.f * delta.y;
if (newRadius > 500 && newRadius < 3000)
{
_cameraRadius = newRadius;
}
}
else
{
float newAspect = _aspect - delta.y * 10;
if (newAspect > -300 && newAspect < 1500)//限制Y轴的高度防止仰角/俯角过大
{
_aspect = newAspect;
}
}
//假设Y=0,x=50.0f * sinf(angle),z=50.0f * cosf(angle), 摄像机的位置为XOZ平面上,原点处半径cameraRadius的圆上
camera->setPosition3D(Vec3(_cameraRadius * sinf(_angle), _aspect, _cameraRadius * cosf(_angle)));
camera->lookAt(Vec3(0.0f, 0.0f, 0.0f), Vec3(0.0f, 1.0f, 0.f));
}
_mouseLatestPosition = mousePosition;
};
_mouseListen->onMouseUp = [this](EventMouse *e) {
if (e->getMouseButton() == EventMouse::MouseButton::BUTTON_LEFT)
{
_isLeftButtonDown = false;
}
_mouseLatestPosition = Vec2(e->getCursorX(), e->getCursorY());
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_mouseListen, 1);
_keyListen = EventListenerKeyboard::create();
_keyListen->onKeyPressed = [this](EventKeyboard::KeyCode code, Event *e) {
if (code == EventKeyboard::KeyCode::KEY_CTRL)
{
_isCrtlKeyDown = true;
}
};
_keyListen->onKeyReleased = [this](EventKeyboard::KeyCode code, Event *e) {
if (code == EventKeyboard::KeyCode::KEY_CTRL)
{
_isCrtlKeyDown = false;
}
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_keyListen, 1);
auto text = Label::createWithSystemFont("", "宋体", 14);
text->setString((char*)u8"按住鼠标左键移动鼠标可旋转视角, 按住Ctrl可移动远近");
text->setPosition(visibleSize.width*0.5 + 100, 20);
this->addChild(text);
return true;
}
void ShadowScene::menuBackCallback(Ref* pSender)
{
Director::getInstance()->getEventDispatcher()->removeEventListener(_mouseListen);
Director::getInstance()->getEventDispatcher()->removeEventListener(_keyListen);
Director::getInstance()->popScene();
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/Kyle12/Cocos2dGraphicEffect.git
git@gitee.com:Kyle12/Cocos2dGraphicEffect.git
Kyle12
Cocos2dGraphicEffect
Cocos2dGraphicEffect
master

搜索帮助