struct Material
{
sampler2D diffuse; //移除amibient材质颜色向量,因为ambient颜色绝大多数情况等于diffuse颜色,所以不需要分别去储存它
sampler2D specular;
float shininess;
}; //材质结构体
uniform Material mater;
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
//lightmap.fsh, 物体的fragment shader, 实现光照贴图
varying vec3 v_normal; //需要把法向量由顶点着色器传递到片段着色器
varying vec3 v_fragpos; //片段的位置,用以计算每个片段的法向量
varying vec2 v_texCoord; //纹理坐标
//uniform vec3 objectColor;
//uniform vec3 lightColor;
uniform vec3 lightpos; //传入灯光的位置
uniform vec3 viewPos; //摄像机位置, 观察者的世界空间坐标,我们简单地使用摄像机对象的位置坐标代替(它就是观察者)
struct Material
{
sampler2D diffuse; //移除amibient材质颜色向量,因为ambient颜色绝大多数情况等于diffuse颜色,所以不需要分别去储存它
sampler2D specular;
float shininess;
}; //材质结构体
uniform Material mater;
struct LightStrength
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
}; //光照强度
uniform LightStrength lsth;
void main()
{
vec3 texdiffuse = vec3(texture2D(mater.diffuse, v_texCoord));
//环境光
vec3 ambient = lsth.ambient * texdiffuse;
//漫反射
vec3 norm = normalize(v_normal);
vec3 lightdir = normalize(lightpos - v_fragpos);
float diff = max(dot(norm, lightdir), 0.0);
vec3 diffuse = lsth.diffuse * diff * texdiffuse;
//镜面反射
float specularStrenth = 0.5; //镜面强度
vec3 viewDir = normalize(viewPos - v_fragpos);
vec3 reflectDir = reflect(-lightdir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), mater.shininess); //这个32是高光的发光值(Shininess)。一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。
vec3 specular = lsth.specular * spec * vec3(texture2D(mater.specular, v_texCoord));
vec3 result = ambient + diffuse + specular;
gl_FragColor = vec4(result, 1.0);
}
GLfloat vertices[] = {
// Positions // Normals // Texture Coords
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
//lightmap.vsh, 物体的vertex shader
attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_texCoord; //纹理坐标
varying vec3 v_normal; //把法向量传给 fragment shader
varying vec3 v_fragpos; //片段的位置
varying vec2 v_texCoord; //纹理坐标
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main(){
gl_Position = projection * view * model * vec4(a_position, 1.0);
v_fragpos = vec3(model * vec4(a_position, 1.0));
v_normal = a_normal;
v_texCoord = a_texCoord;
}
GL::bindTexture2DN(0, diffuseTexture);
glUniform1i(glGetUniformLocation(program->getProgram(), "mater.diffuse"), 0);
一个specular高光的亮度可以通过图片中每个纹理的亮度来获得。specular贴图的每个像素可以显示为一个颜色向量,比如:在那里黑色代表颜色向量vec3(0.0f),灰色是vec3(0.5f)。在片段着色器中,我们采样相应的颜色值,把它乘以光的specular亮度。像素越“白”,乘积的结果越大,物体的specualr部分越亮。
由于箱子几乎是由木头组成,木头作为一个材质不会有镜面高光,整个木头部分的diffuse纹理被用黑色覆盖:黑色部分不会包含任何specular高光。箱子的铁边有一个修改的specular亮度,它自身更容易受到镜面高光影响,木纹部分则不会。
从技术上来讲,木头也有镜面高光,尽管这个闪亮值很小(更多的光被散射),影响很小,但是为了学习目的,我们可以假装木头不会有任何specular光反射。
使用Photoshop或Gimp之类的工具,通过将图片进行裁剪,将某部分调整成黑白图样,并调整亮度/对比度的做法,可以非常容易将一个diffuse纹理贴图处理为specular贴图。
全部代码://
// OpenGLLightingMap.cpp
// shaderTest
//
// Created by MacSBL on 2017/1/10.
//
//
#include "OpenGLLightingMap.h"
bool OpenGLLightingMap::init()
{
if (!Layer::init()) {
return false;
}
origin = Director::getInstance()->getVisibleOrigin();
vsize = Director::getInstance()->getVisibleSize();
GLfloat vertices[] = {
// Positions // Normals // Texture Coords
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
// auto difsprite = Sprite::create("container2.png");
// diffuseTexture = difsprite->getTexture()->getName();
auto spcsprite = Sprite::create("container2_specular.png");
specularTexture = spcsprite->getTexture()->getName();
//以下方法绑定纹理失败,原因: glTexImage2D 中的2个 GL_RGB 都改为 GL_RGBA即可。 跟图片是否有alpha通道有关??
//绑定纹理
glGenTextures(1, &diffuseTexture);
glBindTexture(GL_TEXTURE_2D, diffuseTexture);
//纹理环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//纹理过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//加载纹理
Image* img = new Image();
img->initWithImageFile("container2.png");
GLsizei width = img->getWidth();
GLsizei height = img->getHeight();
unsigned char* imgdata = img->getData();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgdata);
glGenerateMipmap(GL_TEXTURE_2D);
//释放内存,解绑texture
CC_SAFE_DELETE(img);
glBindTexture(GL_TEXTURE_2D, 0);
//——————————————————————————————————————————————————————————————————————————————————————————————
//设置 glprogram
auto program = new GLProgram();
program->initWithFilenames("lightmap.vsh", "lightmap.fsh");
program->link();
this->setGLProgram(program);
//——————————————————————————————————————————————————————————————————————————————————————————————
//物体的vao
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//把顶点数据传给 object.vsh vertex shader 的第一个属性(索引为0)
GLuint posloc = glGetAttribLocation(program->getProgram(), "a_position");
glVertexAttribPointer(posloc, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(posloc);
//把法向量传给 object.vsh 的a_normal属性,注意:这里不能想当然的认为vertex shader中第2个attribute就是索引为1,必须通过program实事求是的取出它的location
GLuint normloc = glGetAttribLocation(program->getProgram(), "a_normal");
glEnableVertexAttribArray(normloc);
glVertexAttribPointer(normloc, 3, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (GLvoid*)(3*sizeof(GLfloat)));
//纹理坐标传给 vertex shader
GLuint texloc = glGetAttribLocation(program->getProgram(), "a_texCoord");
glEnableVertexAttribArray(texloc);
glVertexAttribPointer(texloc, 2, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (GLvoid*)(6*sizeof(GLfloat)));
glBindVertexArray(0);
//灯光的vao, vbo 跟上边一样,灯光和物体使用同一个顶点数组
glGenVertexArrays(1, &lightvao);
glBindVertexArray(lightvao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
//把顶点数据传给 light.vsh vertex shader 的第一个属性(索引为0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
//——————————————————————————————————————————————————————————————————————————————————————————————
lamProgram = new GLProgram();
lamProgram->initWithFilenames("lamp.vsh", "lamp.fsh");
lamProgram->link();
//——————————————————————————————————————————————————————————————————————————————————————————————
//设置转换矩阵
model = new Mat4();
// model->rotate(Vec3(1, 1, 0), CC_DEGREES_TO_RADIANS(60));
//camera
cam = new MyCamera(Vec3(0, 0, 3));
view = cam->GetViewMatrix();
projection = new Mat4();
Mat4::createPerspective(cam->Zoom, vsize.width / vsize.height, 0.1, 1000, projection);
lightpos = Vec3(0.6, 0.5, 0.7);
Director::getInstance()->setDepthTest(true);
//touch事件
auto elistener = EventListenerTouchOneByOne::create();
elistener->onTouchBegan = CC_CALLBACK_2(OpenGLLightingMap::onTouchBegan, this);
elistener->onTouchMoved = CC_CALLBACK_2(OpenGLLightingMap::onTouchMoved, this);
elistener->onTouchEnded = CC_CALLBACK_2(OpenGLLightingMap::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(elistener, this);
return true;
}
void OpenGLLightingMap::visit(cocos2d::Renderer *render, const cocos2d::Mat4 &parentTransform, uint32_t parentflag)
{
Layer::visit(render, parentTransform, parentflag);
_command.init(_globalZOrder);
_command.func = CC_CALLBACK_0(OpenGLLightingMap::onDraw, this);
Director::getInstance()->getRenderer()->addCommand(&_command);
}
void OpenGLLightingMap::onDraw()
{
auto program = this->getGLProgram();
program->use();
//物体的 model、view、project 矩阵
model->scale(1);
view = cam->GetViewMatrix();
Mat4::createPerspective(cam->Zoom, vsize.width / vsize.height, 0.1, 1000, projection);
GLuint modeloc = glGetUniformLocation(program->getProgram(), "model");
glUniformMatrix4fv(modeloc, 1, GL_FALSE, model->m);
GLuint viewloc = glGetUniformLocation(program->getProgram(), "view");
glUniformMatrix4fv(viewloc, 1, GL_FALSE, view->m);
GLuint proloc = glGetUniformLocation(program->getProgram(), "projection");
glUniformMatrix4fv(proloc, 1, GL_FALSE, projection->m);
glUniform3f(glGetUniformLocation(program->getProgram(), "lsth.ambient"), 0.2, 0.2, 0.2);
glUniform3f(glGetUniformLocation(program->getProgram(), "lsth.diffuse"), 0.5, 0.5, 0.5);
glUniform3f(glGetUniformLocation(program->getProgram(), "lsth.specular"), 1.0, 1.0, 1.0);
GL::bindTexture2DN(0, diffuseTexture);
glUniform1i(glGetUniformLocation(program->getProgram(), "mater.diffuse"), 0);
GL::bindTexture2DN(1, specularTexture);
glUniform1i(glGetUniformLocation(program->getProgram(), "mater.specular"), 1);
glUniform1f(glGetUniformLocation(program->getProgram(), "mater.shininess"), 64.0);
GLuint lightposLoc = glGetUniformLocation(program->getProgram(), "lightpos");
glUniform3f(lightposLoc, lightpos.x, lightpos.y, lightpos.z);
GLuint viewposloc = glGetUniformLocation(program->getProgram(), "viewPos");
glUniform3f(viewposloc, cam->Position.x, cam->Position.y, cam->Position.z);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
//灯光的model、view、project 矩阵
lamProgram->use();
modeloc = glGetUniformLocation(lamProgram->getProgram(), "model");
auto lightmodel = new Mat4();
lightmodel->translate(lightpos);
lightmodel->rotate(Vec3(0, 1, 1), CC_DEGREES_TO_RADIANS(30));
lightmodel->scale(0.2);
glUniformMatrix4fv(modeloc, 1, GL_FALSE, lightmodel->m);
viewloc = glGetUniformLocation(lamProgram->getProgram(), "view");
glUniformMatrix4fv(viewloc, 1, GL_FALSE, view->m);
proloc = glGetUniformLocation(lamProgram->getProgram(), "projection");
glUniformMatrix4fv(proloc, 1, GL_FALSE, projection->m);
glBindVertexArray(lightvao);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
}
#pragma mark touch
bool OpenGLLightingMap::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *evt)
{
return true;
}
void OpenGLLightingMap::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *evt)
{
Vec2 curpos = touch->getLocationInView();
Vec2 prepos = touch->getPreviousLocationInView();
GLfloat dx = curpos.x - prepos.x;
GLfloat dy = curpos.y - prepos.y;
//移动摄像机
GLfloat camspeed = 0.05f;
if (curpos.y - prepos.y > 0) { //w
cam->ProcessKeyboard(Cam_Move::FORWARD, camspeed);
}else if (curpos.y - prepos.y < 0){ //s
cam->ProcessKeyboard(Cam_Move::BACKWARD, camspeed);
}
else if (curpos.x - prepos.x < 0){ //a
cam->ProcessKeyboard(Cam_Move::LEFT, camspeed);
}else if (curpos.x - prepos.x > 0){ //d
cam->ProcessKeyboard(Cam_Move::RIGHT, camspeed);
}
//(3)旋转摄像机
// cam->ProcessMouseMovement(dx, dy);
//(4)缩放
// if(fov >= 1 && fov <= 45){
// fov -= dx * camspeed;
// }
// if(fov <= 1){
// fov = 1;
// }
// if(fov >= 45){
// fov = 45;
// }
}
void OpenGLLightingMap::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *evt)
{
}