GL02-01: OpenGL灯光

本主题主要讲解灯光编程,其实灯光与变换一样,因为影响灯光的因素很多,冯氏模型提炼出三种影响因素,并选择了一种数学模型,可以达到较好的视觉效果。
  1. 环境光:环境+物体颜色效果
  2. 漫反射光:光源颜色 + 灯光位置 + 物体颜色
  3. 镜面光:光源颜色 + 观察者位置 + 物体颜色
  4. 冯氏模型:环境因子环境光 + 漫反射因子 漫反射光 + 镜面因子 * 镜面光 = 最终视觉效果


  实际光线复杂在变换带给光线的影响。


灯光模型

  • 现实世界的光照是极其复杂的,而且会受到诸多因素的影响,有人把光照简化成一个简单的数学模型:称为冯氏光照模型(Phong Lighting Model)。冯氏光照模型的主要结构由3个分量组成:

    • 环境(Ambient)光照;
    • 漫反射(Diffuse)光照;
    • 镜面(Specular)光照;
  • 冯氏光照模型的最终输出颜色是:

    • 环境(Ambient) + 漫反射(Diffuse) + 镜面(Specular)光照
  • 备注:

    • 冯氏模型:

冯氏着色和冯氏反射模型是由美国犹他大学(University of Utah)的Bui Tuong Phong于1975年在他的博士论文中提出的,都用其名字命名。

  • 灯光模型主要解决了三种视觉效果:
    • 物体在某个光线环境下的视觉效果;(以静的物体为主体的静态视觉效果)
    • 物体在灯光不同方向照射下的视觉效果;(以灯光位置为主体的动态视觉效果)
    • 观察者在不同角度观察物体的视觉效果;(以观察者为主体的动态视觉效果)

环境光

环境光数学模型

  1. 数学模型:
    • 环境光颜色 \times 3D对象颜色 \times 环境光强弱因子

    • 说明:

      • 环境光是向量的乘法,实际是按照分量相乘。比如(1.0, 0.0,0.0 ,1.0) \times (0.0, 1.0,0.0 ,1.0) = (0.0, 0.0,0.0 ,1.0),得到的黑色光。因为根据光照的物理原理:
        • 在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的颜色。就是那些不能被物体所吸收的颜色就是我们能够感知到的物体的颜色。
        • 比如:红色照在蓝色或者绿色物体上,应该是黑色。

环境光代码实现

  1. 顶点着色器
    • 传递3D对象颜色与顶点坐标;
#version 410 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec4 aColor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 perspective;

out vec4 vColor;
void main(){
    vColor = aColor;
    gl_Position = perspective * view * model * vec4(aPos, 1.0);
}
  1. 片着色器
    • 计算最终的环境光效果输出颜色;
    • 下面的0.2是环境光强弱因子;
#version 410 core
out vec4 FragColor;
in vec4 vColor;
uniform vec4 lightColor;
void main(){
    FragColor =  vColor * lightColor * 0.2f;
}

  1. 没有环境光照与红色环境光照的效果对比(0.4的强度因子)


    环境光效果

漫反射光

漫反射光数学模型

  • 漫反射光是模拟光照的不同角度产生的不同视觉效果。
    • 垂直于物体的光照最亮,反之逐步减弱。
  1. 数学模型
    • cos \alpha \times 灯光颜色 \times 物体颜色
      • cos \alpha 就是漫反射因子。
    • 光的强度(漫反射因子大小)依赖光线与物体的角度,为了表示这个角度,约定了1个数学上的几何名词:
      • 法向量(垂直于物体表面光的方向,该向量取单位向量)
    • 法向量与光线方向的夹角越大,光线越弱。
      • 使用角度的余弦(余弦函数在0-90°区间是反单调的,90°时为0,0°为1)
      • 夹角的余弦在单位向量的情况下,刚好是两个向量的内积(俗称点积)
漫反射的数学模型
  1. 变换因素的考虑
    • 因为物体最终的视觉效果还受Model,View与Perspective三个矩阵的影响,

漫反射光的代码实现

  • 漫反射光需要的数据条件:
    • 光源位置:lightPos;
    • 光源颜色:lightColor;
  1. 顶点着色器
    • 传递了物体坐标给片着色器,用于计算光源方向。
#version 410 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 perspective;
out vec3 vPos;
void main(){
    vPos = aPos;
    gl_Position = perspective * view * model * vec4(aPos, 1.0);
}

  1. 片着色器
    • 使用unform传递了两个数据
      • 光源颜色:uniform vec4 lightColor;
      • 光源位置:uniform vec3 lightPos;
#version 410 core
uniform vec3 lightPos;
uniform vec4 lightColor;

in vec3 vPos;
out vec4 FragColor;

void main(){
    // 计算光源的方向
    vec3 lightDir =  normalize(lightPos - vPos);    // 单位化
    // 计算漫反射因子
    float diffuse = max(dot(lightDir, vPos), 0);    // max是防止内积为负数
    // 计算漫反射光
    vec4 diffuseColor = diffuse * lightColor;
    FragColor =  diffuseColor;
}


  1. 数据传递
    • 传递了光源位置与颜色。
    shader.initProgram();
    shader.compileShaderFromFile("./glsl_script/glsl05_v_diffuse.glsl", GL_VERTEX_SHADER);
    shader.compileShaderFromFile("./glsl_script/glsl05_f_diffuse.glsl", GL_FRAGMENT_SHADER);
    shader.link();
    // 传递光源位置
    glm::vec3 lightPos = glm::vec3(1.0f, 1.0f, 1.0f);
    shader.setUniform_3fv("lightPos", glm::value_ptr(lightPos));
    // 传递光的颜色
    glm::vec4 lightColor = glm::vec4(1.0f, 0.5f, 0.5f, 1.0f);
    shader.setUniform_4fv("lightColor", glm::value_ptr(lightColor));
  1. 效果
漫反射效果

改进

添加环境光

  1. 片着色器
    • 强化了漫发射光,弱化了环境光。
#version 410 core
uniform vec3 lightPos;
uniform vec4 lightColor;

in vec3 vPos;
out vec4 FragColor;

void main(){
    // 计算光源的方向
    vec3 lightDir =  normalize(lightPos - vPos);    // 单位化
    // 计算漫反射因子
    float diffuse = max(dot(lightDir, vPos), 0);    // max是防止内积为负数
    // 计算漫反射光
    vec4 diffuseColor = diffuse * lightColor;
    // FragColor =  diffuseColor;

    // 改进:添加环境光
    vec4 ambientColor = vec4(1.0f, 1.0f, 0.0f, 1.0f);   // 环境光
    float ambientFactor =0.4;   // 环境光因子
    FragColor = (ambientFactor * ambientColor + 2.0 *  diffuseColor) * vec4(vPos, 1.0f);
}

  1. 效果
环境光 + 漫反射

考虑物体Model变换的影响

  • 上面光源没有变化,但漫反射产生的效果感觉光源位置在变化,这实际是物体的顶点在做世界坐标变换的时候,实际上计算法向量的时候没有考虑(就是计算光源方向的时候,没有考虑物体的世界变换)。
  1. 代码
#version 410 core
uniform vec3 lightPos;
uniform vec4 lightColor;
uniform mat4 model;

in vec3 vPos;
out vec4 FragColor;

void main(){
    // 改进2:不直接使用vPos,而是使用世界变换后的顶点坐标
    // 先补一个分量,完成计算后,再转换为vec3,这样世界变换中的位移没有影响
    vec3 modelPos = vec3(model * vec4(vPos, 1.0));   
    vec3 lightDir =  normalize(lightPos - modelPos);    // 单位化
    // 计算光源的方向
    // vec3 lightDir =  normalize(lightPos - vPos);    // 单位化
    // 计算漫反射因子
    float diffuse = max(dot(lightDir, modelPos), 0);    // max是防止内积为负数
    // float diffuse = max(dot(lightDir, vPos), 0);    // max是防止内积为负数
    // 计算漫反射光
    vec4 diffuseColor = diffuse * lightColor;
    // FragColor =  diffuseColor;

    // 改进1:添加环境光
    vec4 ambientColor = vec4(1.0f, 1.0f, 0.0f, 1.0f);   // 环境光
    float ambientFactor =0.4;   // 环境光因子
    FragColor = (ambientFactor * ambientColor + 2.0 *  diffuseColor) * vec4(vPos, 1.0f);
}

  1. 效果
漫反射+ 变换
  • 关于其他
    • 实际光源还受很多因素影响,比如变换中的缩放等,照相机位置等。这些根据同样原理,牢牢关注方向的计算方式,然后调整对应的变换矩阵即可(当然这个矩阵可能会很复杂)。
    • 这里我们假设球体是在原点,这样顶点坐标就是法向量(法向量就是顶点坐标与原点的连线),对不同情况下的球体法向量有标准求法-结果也是切面点与圆心的连线向量;

改进-变换与背面剔除

变换

  1. model
  2. view
  3. perspective
   glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 2.0f);
    glm::mat4 view = glm::lookAt(
        cameraPos,      // 照相机位置
        glm::vec3(0.0f, 0.0f, 0.0f),    // 物体位置,暂时设置为原点
        glm::vec3(0.0f, 1.0f, 0.0f));   // 相机向上的向量,计算右向量的条件
    glm::mat4 perspective = glm::perspective(glm::radians(90.0f), 1.0f, 0.0f, 100.0f);

    model = glm::scale(model, glm::vec3(0.5f, 0.5f, 0.5f));
    model = glm::rotate(model, glm::radians(0.0f), glm::vec3(1.0f, 0.0f, 0.0f));
    shader.setUniform_4fm("model", glm::value_ptr(model));
    shader.setUniform_4fm("view", glm::value_ptr(view));
    shader.setUniform_4fm("perspective", glm::value_ptr(perspective));
  • 其中model在每次绘制的时候旋转,产生动态效果

声明:

glm::mat4  model        = glm::mat4(1.0f);

旋转:

void render(){
    // glCullFace(GL_BACK);
    model = glm::rotate(model, glm::radians(1.0f), glm::vec3(1.0f, 0.0f, 0.0f));
    shader.setUniform_4fm("model", glm::value_ptr(model));

    glClearDepth (0.0f);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    shader.open();
    data.open();
    glPointSize(5.0f);
    // glDrawArrays(GL_POINTS, 0, 6 * lats * lons);
    glDrawArrays(GL_TRIANGLES, 0, 6 * lats * lons);
    // glDrawArrays(GL_LINE_LOOP, 0, 6 * lats * lons);
    data.close();
    shader.close();
}

背面剔除

  • 关键代码:
    ///////////////////////////////////////////////////
    // 开启深度缓冲
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_GEQUAL);
    // 背面剔除
    glEnable(GL_CULL_FACE);    // 开启
    glCullFace (GL_BACK);      // 剔除背面
    ///////////////////////////////////////////////////
  • 剔除背面的前后比较
剔除背面的前后比较

附录

球体表面的法向量求法

  • 球面坐标表示

    • 首先使用u来表示球面的点与z轴的夹角,v表示球面点在xy平面上投影点与x轴的夹角。这样球面坐标表示为:
      • x = x_o + r * sin(u) \cdot cos(v)
      • y = y_o + r * sin(u) \cdot sin(v)
      • z = z_o + r * cos(u)
  • u,v的偏导数,得到u, v的求曲面切线(x_u, y_u, z_u), (x_v, y_v, z_v),两个切线的叉乘就是法向量。

    • 球面法向量为(x_u, y_u, z_u) \times (x_v, y_v, z_v) = A * \pmatrix{sin(u) \cdot cos(v) \\sin(u) \cdot sin(v) \\cos(u)}
    • 其中A = r ^2 sin(v)
  • 其实球面点的法向量就是其本身,只是范数有变化,这个标准化以后就没有差异了。

镜面光

镜面光数学模型

  • 镜面光是模拟观察者在不同角度观察产生的视觉效果变化。

  • 镜面光也好,漫反射光也好,环境光也好,观察者最终只有看到一个效果:这个效果是各种反射的混合;

  • 真正影响颜色输出的两个因素:

    1. 光线颜色,环境颜色,物体颜色;
    2. 每种颜色的强弱,或者在最后混合中的比例,这种比例不同就产生各种神奇的视觉效果。
  • 环境光:

    • 处理最简单,直接一个常数因子 * 环境光颜色,常数因子取值最好0-1之间。
  • 漫反射光:

    • 灯光颜色 * 漫反射因子。漫反射因子大小与光源的位置有关,这样光线在不同位置,就产生不同效果。
    • 漫反射因子就是光线方向与物体垂直方向的夹角的余弦(这是选择,你可以考虑不使用余弦,只要是反单调,并且取值在0-1之间就行)
  • 镜面光:

    • 灯光颜色 * 镜面因子。镜面因子大小与照相机位置有关,还与光源在物体上的反射方向有关(反射方向也与物体法向量有关),照相机位置变化就会产生不同的视觉效果。
    • 镜面因子也是夹角余弦(这是冯氏模型的选择),只是夹角是照相机方向向量与光线的反射光方向向量的夹角。
  • 镜面光示意图:


    镜面光示意图
  • 注意:
    • 镜面光的难度是求反向光的向量。
    • 反向光的向量计算数学模型是对称的模型,实际就是一个旋转变换(在GLSL与GLM都有实现),具体的推导交给数学家去玩。

镜面管的代码实现

  • 镜面光需要增加一个条件:照相机的位置。
  1. 传动照相机位置
    glm::vec3 cameraPos     = glm::vec3(0.0f, 0.0f, 3.0f);
    // 传送照相机位置
    shader.setUniform_3fv("cameraPos",glm::value_ptr(cameraPos));
  1. 片着色器
    • 其中使用指数pow函数,因为镜面光因子小于0,指数运算会降低镜面因子,亮度降低,但在合成的时候,使用了增强系数。
    • 为了效果,把灯光设置为白色,环境光设置为红色;(可以试着调整参数,并明白其中的影响关系)
#version 410 core
uniform vec3 lightPos;
uniform vec4 lightColor;
uniform mat4 model;
uniform vec3 cameraPos;

in vec3 vPos;
out vec4 FragColor;

void main(){
    vec3 modelPos = vec3(model * vec4(vPos, 1.0));   
    vec3 lightDir =  normalize(lightPos - modelPos);    
    float diffuse = max(dot(lightDir, modelPos), 0);  
    vec4 diffuseColor = diffuse * lightColor;

    vec4 ambientColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);  
    float ambientFactor =0.2;  
    // 计算照相机到物体的方向
    vec3 cameraDir = normalize(cameraPos - modelPos);
    // 计算光源的反射光向量
    vec3 reflectDir = reflect(lightDir, modelPos);
    // 计算反向光向量与照相机方向向量的内积
    float specular = pow(max(dot(cameraDir, reflectDir), 0.0), 4);
    // 镜面光输出颜色
    vec4 specularColor = specular * lightColor;

    FragColor = (ambientFactor * ambientColor + 0.4 * diffuseColor + 3.0f * specularColor) * vec4(1.0f, 1.0f, 1.0f, 1.0f);
}

3.变换数据发送

#include "gl_env.h"
#include "gl_data.h"
#include "gl_shader.h"
#include <iostream>
#include <glm/glm.hpp>   //glm::vec3, glm::vec4, glm::mat4
#include <glm/ext.hpp>  //glm::translate, glm::rotate, glm::scale, glm::perspective, glm::pi
#include <glm/gtc/type_ptr.hpp>    // 把glm结构体转换为指针
#include <cmath>

// 环境
GL_Env env;
// 定点数组
GL_Data data;
// 着色器
GL_Shader shader;
glm::mat4  model        = glm::mat4(1.0f);
GLuint lats = 30;
GLuint lons = 60;
void render();

/**
 * 生成球面顶点,半径是是单位长度1;
 */
glm::vec3  getPoint(GLfloat u, GLfloat v){
    GLfloat r = 0.9f;
    GLfloat pi = glm::pi<GLfloat>();
    GLfloat z = r * std::cos(pi * u);
    GLfloat x = r * std::sin(pi * u) * std::cos(2 * pi * v);
    GLfloat y = r * std::sin(pi * u) * std::sin(2 * pi * v);
    // std::cout << x << "," << y << "," << z << std::endl;
    return glm::vec3(x, y, z); 
}

void createSphere(GLfloat *sphere, GLuint Longitude, GLuint Latitude){
    // Longitude:经线切分个数
    // Latitude:纬线切分个数
    GLfloat lon_step = 1.0f/Longitude;
    GLfloat lat_step = 1.0f/Latitude;
    GLuint offset = 0;
    for(int lat = 0; lat < Latitude; lat++){  // 纬线u
        for(int lon = 0;lon < Longitude; lon++){ // 经线v
            // 一次构造4个点,两个三角形,
            glm::vec3 point1 = getPoint(lat * lat_step, lon * lon_step);
            glm::vec3 point2 = getPoint((lat + 1) * lat_step, lon * lon_step);
            glm::vec3 point3 = getPoint((lat + 1) * lat_step, (lon + 1) * lon_step);
            glm::vec3 point4 = getPoint(lat * lat_step, (lon + 1) * lon_step);
            memcpy(sphere + offset, glm::value_ptr(point1), 3 * sizeof(GLfloat));
            offset += 3;
            memcpy(sphere + offset, glm::value_ptr(point4), 3 * sizeof(GLfloat));
            offset += 3;
            memcpy(sphere + offset, glm::value_ptr(point3), 3 * sizeof(GLfloat));
            offset += 3;

            memcpy(sphere + offset, glm::value_ptr(point1), 3 * sizeof(GLfloat));
            offset += 3;
            memcpy(sphere + offset, glm::value_ptr(point3), 3 * sizeof(GLfloat));
            offset += 3;
            memcpy(sphere + offset, glm::value_ptr(point2), 3 * sizeof(GLfloat));
            offset += 3;
        } 
    }
    // std::cout<<"offset:" << offset << std::endl;
}

int main(int argc, const char** argv) {

    env.context("GLSL语言-镜面光");
    env.opengl();
    ///////////////////////////////////////////////////
    data.initVertextArrays();
    

    GLfloat vertices[6 * 3 * lats * lons ]; 
    createSphere(vertices, lons, lats);
    data.addData(vertices, sizeof(vertices), 0, 3);     // 开启Shader第一个参数位置,顶点3个一组。
    
    ///////////////////////////////////////////////////
    shader.initProgram();
    shader.compileShaderFromFile("./glsl_script/glsl07_v_specular.glsl", GL_VERTEX_SHADER);
    shader.compileShaderFromFile("./glsl_script/glsl07_f_specular.glsl", GL_FRAGMENT_SHADER);
    shader.link();
    // 传递光源位置
    glm::vec3 lightPos = glm::vec3(-5.0f, 5.0f, 0.0f);
    shader.setUniform_3fv("lightPos", glm::value_ptr(lightPos));
    // 传递光的颜色
    glm::vec4 lightColor = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
    shader.setUniform_4fv("lightColor", glm::value_ptr(lightColor));

    // glm::mat4  view         = glm::mat4(1.0f);
    // glm::mat4  perspective  = glm::mat4(1.0f);
    // 添加变换
    glm::vec3 cameraPos     = glm::vec3(3.0f, 0.0f, 0.0f);
    // 传送照相机位置
    shader.setUniform_3fv("cameraPos",glm::value_ptr(cameraPos));
    glm::mat4  view         = glm::lookAt(
        cameraPos,
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 1.0f, 0.0f));
    glm::mat4  perspective  = glm::perspective(
        glm::radians(90.0f),
        1.0f,
        0.0f,100.0f);

    model = glm::rotate(model, glm::radians(0.0f), glm::vec3(1.0f, 0.0f, 0.0f));
    shader.setUniform_4fm("model", glm::value_ptr(model));
    shader.setUniform_4fm("view", glm::value_ptr(view));
    shader.setUniform_4fm("perspective", glm::value_ptr(perspective));

    ///////////////////////////////////////////////////
    // 开启深度缓冲
    glEnable(GL_CULL_FACE);
    glCullFace (GL_BACK);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_GEQUAL);
    ///////////////////////////////////////////////////
    env.run(render);
    env.destroy();
    return 0;
}
void render(){
    // glCullFace(GL_BACK);
    model = glm::rotate(model, glm::radians(1.0f), glm::vec3(1.0f, 0.0f, 0.0f));
    shader.setUniform_4fm("model", glm::value_ptr(model));

    glClearDepth (0.0f);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    shader.open();
    data.open();
    glPointSize(5.0f);
    // glDrawArrays(GL_POINTS, 0, 6 * lats * lons);
    glDrawArrays(GL_TRIANGLES, 0, 6 * lats * lons);
    // glDrawArrays(GL_LINE_LOOP, 0, 6 * lats * lons);
    data.close();
    shader.close();
}
// g++ -omain  gl07_specular.cpp gl_env.cpp gl_data.cpp gl_shader.cpp  -lglfw  -lglew  -framework opengl

  1. 效果
镜面光效果

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容