光线追踪(RayTracing)算法理论与实践(三)光照

分类: 计算机图形学2012-12-05 00:09 2360人阅读 评论(0) 收藏 举报

目录(?)[+]

提要

经过之前的学习,我们已经可以在利用光线追踪实现一些简单的场景。今天我们要探讨的是图形学里面的三种基本光源:方向光源,点光源,聚光灯。

不同于利用现成的Api,这次会从理论到实际一步步用C++实现。

前提工作

在老师的建议下,我将图形引擎换成了SDL,最终的渲染效果比之前的好了很多,原来的GLFW虽然能够很好的兼容OpenGL,但并没提供对像素的控制,而SDL有Surface。

对与GLFW,本人觉得其终究只能算是glut的替代品,而SDL应当是一个完善的游戏引擎,而且文档和教程都非常地丰富。

有关SDL的文章,请猛击这里。

方向光源

方向光源是一组平行光。所以方向光源类只有方向和颜色两个属性。用一个向量对象来表示方向,颜色对象表示光的颜色。

阴影

回忆一下入门文章的第一幅图片,在有光的情况下,判断某一点是否是阴影,即判断是否能够从那一点看到光。

那么光线追踪的过程就是:

从摄像机产生光线->投射场景->若与物体相交,从该点产生光线,方向为光源方向的饭方向->投射场景->若与场景中的物体相交,则属于阴影区域。

方向光源的实现:

[cpp] view plaincopy
  1. /*****************************************************************************
  2. Copyright: 2012, ustc All rights reserved.
  3. contact:k283228391@126.com
  4. File name: directlight.h
  5. Description:directlight's h doc.
  6. Author:Silang Quan
  7. Version: 1.0
  8. Date: 2012.12.04
  9. *****************************************************************************/
  10. #ifndef DIRECTLIGHT_H
  11. #define DIRECTLIGHT_H
  12. #include "color.h"
  13. #include "gvector3.h"
  14. #include "union.h"
  15. class DirectLight
  16. {
  17. public:
  18. DirectLight();
  19. DirectLight(Color _color,GVector3 _direction,bool _isShadow);
  20. virtual ~DirectLight();
  21. Color intersect(Union &scence,IntersectResult &result);
  22. protected:
  23. private:
  24. bool isShadow;
  25. Color color;
  26. GVector3 direction;
  27. };
  28. #endif // DIRECTLIGHT_H
[cpp] view plaincopy
  1. /*****************************************************************************
  2. Copyright: 2012, ustc All rights reserved.
  3. contact:k283228391@126.com
  4. File name: directlight.cpp
  5. Description:directlight's cpp doc.
  6. Author:Silang Quan
  7. Version: 1.0
  8. Date: 2012.12.04
  9. *****************************************************************************/
  10. #include "directlight.h"
  11. DirectLight::DirectLight()
  12. {
  13. //ctor
  14. }
  15. DirectLight::DirectLight(Color _color,GVector3 _direction,bool _isShadow)
  16. {
  17. color=_color;
  18. direction=_direction;
  19. isShadow=_isShadow;
  20. }
  21. DirectLight::~DirectLight()
  22. {
  23. //dtor
  24. }
  25. //通过光线与场景的相交结果计算光照结果
  26. Color DirectLight::intersect(Union &scence,IntersectResult &rayResult)
  27. {
  28. //生产shadowRay的修正值
  29. const float k=1e-4;
  30. //生成与光照相反方向的shadowRay
  31. GVector3 shadowDir=direction.normalize().negate();
  32. CRay shadowRay=CRay(rayResult.position+rayResult.normal*k,shadowDir);
  33. //计算shadowRay是否与场景相交
  34. IntersectResult lightResult = scence.isIntersected(shadowRay);
  35. Color resultColor = Color::black();
  36. if(isShadow)
  37. {
  38. if(lightResult.object)
  39. {
  40. return resultColor;
  41. }
  42. }
  43. //计算光强
  44. float NdotL=rayResult.normal.dotMul(shadowDir);
  45. if (NdotL >= 0)
  46. resultColor=resultColor.add(this->color.multiply(NdotL));
  47. //return this->color;
  48. return resultColor;
  49. }

需要注意的是intersect函数,输入的参数是场景的引用和光线和场景相交结果的引用,返回一个Color。

若shadowRay没有与场景相交,那么就要对那一点接收到的光强进行计算。

与之有关的就是平面法向量与光的方向的夹角,当这个夹角约大,接受的光强就越小,想想看,中午太阳光是不是最强,傍晚是不是比较弱一些:0).

计算夹角利用的是向量的点乘。

渲染一下:

[cpp] view plaincopy
  1. void renderLight()
  2. {
  3. Uint32 pixelColor;
  4. Union scene;
  5. PerspectiveCamera camera( GVector3(0, 10, 10),GVector3(0, 0, -1),GVector3(0, 1, 0), 90);
  6. Plane* plane1=new Plane(GVector3(0, 1, 0),0.0);
  7. Plane* plane2=new Plane(GVector3(0, 0, 1),-50);
  8. Plane* plane3=new Plane(GVector3(1, 0, 0),-20);
  9. CSphere* sphere1=new CSphere(GVector3(0, 10, -10), 10.0);
  10. DirectLight light1(Color::white().multiply(10), GVector3(-1.75, -2, -1.5),true);
  11. scene.push(plane1);
  12. scene.push(plane2);
  13. scene.push(plane3);
  14. scene.push(sphere1);
  15. long maxDepth=20;
  16. float dx=1.0f/WINDOW_WIDTH;
  17. float dy=1.0f/WINDOW_HEIGHT;
  18. float dD=255.0f/maxDepth;
  19. for (long y = 0; y < WINDOW_HEIGHT; ++y)
  20. {
  21. float sy = 1 - dy*y;
  22. for (long x = 0; x < WINDOW_WIDTH; ++x)
  23. {
  24. float sx =dx*x;
  25. CRay ray(camera.generateRay(sx, sy));
  26. IntersectResult result = scene.isIntersected(ray);
  27. if (result.isHit)
  28. {
  29. Color color=light1.intersect(scene,result);
  30. pixelColor=SDL_MapRGB(screen->format,std::min(color.r*255,(float)255),std::min(color.g*255,(float)255.0),std::min(color.b*255,(float)255.0));
  31. drawPixel(screen, x, y,pixelColor);
  32. }
  33. }
  34. }
  35. }

点光源

点光源/点光灯(point light),又称全向光源/泛光源/泛光灯(omnidirectional light/omni light),是指一个无限小的点,向所有光向平均地散射光。最常见的点光源就是电灯泡了,需要确定光源的位置,还有就是光的颜色。

在计算光强的时候,需要乘以一个衰减系数,接收到的能量和距离的关系,是成平方反比定律的:

点光源的实现:

[cpp] view plaincopy
  1. /*****************************************************************************
  2. Copyright: 2012, ustc All rights reserved.
  3. contact:k283228391@126.com
  4. File name: pointlight.h
  5. Description:pointlight's h doc.
  6. Author:Silang Quan
  7. Version: 1.0
  8. Date: 2012.12.04
  9. *****************************************************************************/
  10. #ifndef POINTLIGHT_H
  11. #define POINTLIGHT_H
  12. #include "color.h"
  13. #include "gvector3.h"
  14. #include "union.h"
  15. class PointLight
  16. {
  17. public:
  18. PointLight();
  19. PointLight(Color _color,GVector3 _position,bool _isShadow);
  20. virtual ~PointLight();
  21. Color intersect(Union &scence,IntersectResult &result);
  22. protected:
  23. private:
  24. bool isShadow;
  25. Color color;
  26. GVector3 position;
  27. };
  28. #endif // POINTLIGHT_H
[cpp] view plaincopy
  1. /*****************************************************************************
  2. Copyright: 2012, ustc All rights reserved.
  3. contact:k283228391@126.com
  4. File name: pointlight.cpp
  5. Description:pointlight's cpp doc.
  6. Author:Silang Quan
  7. Version: 1.0
  8. Date: 2012.12.04
  9. *****************************************************************************/
  10. #include "pointlight.h"
  11. PointLight::PointLight()
  12. {
  13. //ctor
  14. }
  15. PointLight::~PointLight()
  16. {
  17. //dtor
  18. }
  19. PointLight::PointLight(Color _color,GVector3 _position,bool _isShadow)
  20. {
  21. color=_color;
  22. position=_position;
  23. isShadow=_isShadow;
  24. }
  25. //通过光线与场景的相交结果计算光照结果
  26. Color PointLight::intersect(Union &scence,IntersectResult &rayResult)
  27. {
  28. //生产shadowRay的修正值
  29. const float k=1e-4;
  30. GVector3 delta=this->position-rayResult.position;
  31. float distance=delta.getLength();
  32. //生成与光照相反方向的shadowRay
  33. CRay shadowRay=CRay(rayResult.position,delta.normalize());
  34. GVector3 shadowDir=delta.normalize();
  35. //计算shadowRay是否与场景相交
  36. IntersectResult lightResult = scence.isIntersected(shadowRay);
  37. Color resultColor = Color::black();
  38. Color returnColor=Color::black();
  39. //如果shadowRay与场景中的物体相交
  40. if(lightResult.object&&(lightResult.distance<=distance))
  41. {
  42. return resultColor;;
  43. }
  44. else
  45. {
  46. resultColor=this->color.divide(distance*distance);
  47. float NdotL=rayResult.normal.dotMul(shadowDir);
  48. if (NdotL >= 0)
  49. returnColor=returnColor.add(resultColor.multiply(NdotL));
  50. return returnColor;
  51. }
  52. }

渲染一下:

在rendeLight函数中初始化点光源:

[cpp] view plaincopy
  1. PointLight light2(Color::white().multiply(200), GVector3(10,20,10),true);

聚光灯

聚光灯点光源的基础上,加入圆锥形的范围,最常见的聚光灯就是手电了,或者舞台的投射灯。聚光灯可以有不同的模型,以下采用Direct3D固定功能管道(fixed-function pipeline)用的模型做示范。

聚光灯有一个主要方向s,再设置两个圆锥范围,称为内圆锥和外圆锥,两圆锥之间的范围称为半影(penumbra)。内外圆锥的内角分别为和。聚光灯可计算一个聚光灯系数,范围为[0,1],代表某方向的放射比率。内圆锥中系数为1(最亮),内圆锥和外圆锥之间系数由1逐渐变成0。另外,可用另一参数p代表衰减(falloff),决定内圆锥和外圆锥之间系数变化。方程式如下:

聚光灯的实现

[cpp] view plaincopy
  1. /*****************************************************************************
  2. Copyright: 2012, ustc All rights reserved.
  3. contact:k283228391@126.com
  4. File name: spotlight.h
  5. Description:spotlight's h doc.
  6. Author:Silang Quan
  7. Version: 1.0
  8. Date: 2012.12.04
  9. *****************************************************************************/
  10. #ifndef SPOTLIGHT_H
  11. #define SPOTLIGHT_H
  12. #include "color.h"
  13. #include "gvector3.h"
  14. #include "union.h"
  15. #include <math.h>
  16. class SpotLight
  17. {
  18. public:
  19. SpotLight();
  20. SpotLight(Color _color,GVector3 _position,GVector3 _direction,float _theta,float _phi,float _fallOff,bool _isShadow);
  21. virtual ~SpotLight();
  22. Color intersect(Union &scence,IntersectResult &result);
  23. protected:
  24. private:
  25. Color color;
  26. GVector3 position;
  27. GVector3 direction;
  28. bool isShadow;
  29. float theta;
  30. float phi;
  31. float fallOff;
  32. //negate the Direction
  33. GVector3 directionN;
  34. float cosTheta;
  35. float cosPhi;
  36. float baseMultiplier;
  37. };
  38. #endif // SPOTLIGHT_H
[cpp] view plaincopy
  1. /*****************************************************************************
  2. Copyright: 2012, ustc All rights reserved.
  3. contact:k283228391@126.com
  4. File name: pointlight.cpp
  5. Description:pointlight's cpp doc.
  6. Author:Silang Quan
  7. Version: 1.0
  8. Date: 2012.12.04
  9. *****************************************************************************/
  10. #include "pointlight.h"
  11. PointLight::PointLight()
  12. {
  13. //ctor
  14. }
  15. PointLight::~PointLight()
  16. {
  17. //dtor
  18. }
  19. PointLight::PointLight(Color _color,GVector3 _position,bool _isShadow)
  20. {
  21. color=_color;
  22. position=_position;
  23. isShadow=_isShadow;
  24. }
  25. //通过光线与场景的相交结果计算光照结果
  26. Color PointLight::intersect(Union &scence,IntersectResult &rayResult)
  27. {
  28. //生产shadowRay的修正值
  29. const float k=1e-4;
  30. GVector3 delta=this->position-rayResult.position;
  31. float distance=delta.getLength();
  32. //生成与光照相反方向的shadowRay
  33. CRay shadowRay=CRay(rayResult.position,delta.normalize());
  34. GVector3 shadowDir=delta.normalize();
  35. //计算shadowRay是否与场景相交
  36. IntersectResult lightResult = scence.isIntersected(shadowRay);
  37. Color resultColor = Color::black();
  38. Color returnColor=Color::black();
  39. //如果shadowRay与场景中的物体相交
  40. if(lightResult.object&&(lightResult.distance<=distance))
  41. {
  42. return resultColor;;
  43. }
  44. else
  45. {
  46. resultColor=this->color.divide(distance*distance);
  47. float NdotL=rayResult.normal.dotMul(shadowDir);
  48. if (NdotL >= 0)
  49. returnColor=returnColor.add(resultColor.multiply(NdotL));
  50. return returnColor;
  51. }
  52. }

渲染一下:

在场景中初始化一个聚光灯:

[cpp] view plaincopy
  1. SpotLight light3(Color::white().multiply(1350),GVector3(30, 30, 20),GVector3(-1, -0.7, -1), 20, 30, 0.5,true);

渲染多个灯

这里用到了vector容器。场景中布置了很多个点光源,渲染耗时将近半分钟。

[cpp] view plaincopy
  1. void renderLights()
  2. {
  3. Uint32 pixelColor;
  4. Union scene;
  5. PerspectiveCamera camera( GVector3(0, 10, 10),GVector3(0, 0, -1),GVector3(0, 1, 0), 90);
  6. Plane* plane1=new Plane(GVector3(0, 1, 0),0.0);
  7. Plane* plane2=new Plane(GVector3(0, 0, 1),-50);
  8. Plane* plane3=new Plane(GVector3(1, 0, 0),-20);
  9. CSphere* sphere1=new CSphere(GVector3(0, 10, -10), 10.0);
  10. CSphere* sphere2=new CSphere(GVector3(5, 5, -7), 3.0);
  11. PointLight *light2;
  12. vector<PointLight> lights;
  13. for (int x = 10; x <= 30; x += 4)
  14. for (int z = 20; z <= 40; z += 4)
  15. {
  16. light2=new PointLight(Color::white().multiply(80),GVector3(x, 50, z),true);
  17. lights.push_back(*light2);
  18. }
  19. scene.push(plane1);
  20. scene.push(plane2);
  21. scene.push(plane3);
  22. scene.push(sphere1);
  23. //scene.push(sphere2);
  24. long maxDepth=20;
  25. float dx=1.0f/WINDOW_WIDTH;
  26. float dy=1.0f/WINDOW_HEIGHT;
  27. float dD=255.0f/maxDepth;
  28. for (long y = 0; y < WINDOW_HEIGHT; ++y)
  29. {
  30. float sy = 1 - dy*y;
  31. for (long x = 0; x < WINDOW_WIDTH; ++x)
  32. {
  33. float sx =dx*x;
  34. CRay ray(camera.generateRay(sx, sy));
  35. IntersectResult result = scene.isIntersected(ray);
  36. if (result.isHit)
  37. {
  38. Color color=Color::black();
  39. for(vector<PointLight>::iterator iter=lights.begin();iter!=lights.end();++iter)
  40. {
  41. color=color.add(iter->intersect(scene,result));
  42. }
  43. pixelColor=SDL_MapRGB(screen->format,std::min(color.r*255,(float)255),std::min(color.g*255,(float)255.0),std::min(color.b*255,(float)255.0));
  44. drawPixel(screen, x, y,pixelColor);
  45. }
  46. }
  47. }
  48. }

渲染结果:

渲染三原色

把原先场景中的球体去掉,布置3盏聚光动,发射红绿蓝,可以很清晰地看见它们融合之后的颜色。

[cpp] view plaincopy
  1. void renderTriColor()
  2. {
  3. Uint32 pixelColor;
  4. Union scene;
  5. PerspectiveCamera camera( GVector3(0, 40, 15),GVector3(0, -1.25, -1),GVector3(0, 1, 0), 60);
  6. Plane* plane1=new Plane(GVector3(0, 1, 0),0.0);
  7. Plane* plane2=new Plane(GVector3(0, 0, 1),-50);
  8. Plane* plane3=new Plane(GVector3(1, 0, 0),-20);
  9. PointLight light0(Color::white().multiply(1000), GVector3(30,40,20),true);
  10. SpotLight light1(Color::red().multiply(2000),GVector3(0, 30, 10),GVector3(0, -1, -1), 20, 30, 1,true);
  11. SpotLight light2(Color::green().multiply(2000),GVector3(6, 30, 20),GVector3(0, -1, -1), 20, 30, 1,true);
  12. SpotLight light3(Color::blue().multiply(2000),GVector3(-6, 30, 20),GVector3(0, -1, -1), 20, 30, 1,true);
  13. scene.push(plane1);
  14. scene.push(plane2);
  15. scene.push(plane3);
  16. long maxDepth=20;
  17. float dx=1.0f/WINDOW_WIDTH;
  18. float dy=1.0f/WINDOW_HEIGHT;
  19. float dD=255.0f/maxDepth;
  20. for (long y = 0; y < WINDOW_HEIGHT; ++y)
  21. {
  22. float sy = 1 - dy*y;
  23. for (long x = 0; x < WINDOW_WIDTH; ++x)
  24. {
  25. float sx =dx*x;
  26. CRay ray(camera.generateRay(sx, sy));
  27. IntersectResult result = scene.isIntersected(ray);
  28. if (result.isHit)
  29. {
  30. Color color=light0.intersect(scene,result);
  31. color=color.add(light1.intersect(scene,result));
  32. color=color.add(light2.intersect(scene,result));
  33. color=color.add(light3.intersect(scene,result));
  34. pixelColor=SDL_MapRGB(screen->format,std::min(color.r*255,(float)255),std::min(color.g*255,(float)255.0),std::min(color.b*255,(float)255.0));
  35. drawPixel(screen, x, y,pixelColor);
  36. }
  37. }
  38. }
  39. }

渲染结果

结语

花了大概一周的时间来实现这个光照效果,虽然网上有相关文章,但亲自动手来实现又是另外一回事了。

当然,这都没有结束,期待后续。

raytrace 算法理论与实践相关推荐

  1. 万字长文详解文本抽取:从算法理论到实践

    导读:"达观杯"文本智能信息抽取挑战赛已吸引来自中.美.英.法.德等26个国家和地区的2400余名选手参赛,目前仍在火热进行中(点击阅读原文进入比赛页面,QQ群见上图或文末二维码) ...

  2. ​万字长文详解文本抽取:从算法理论到实践(附“达观杯”官方baseline实现解析及答疑)...

    [ 导读 ]"达观杯"文本智能信息抽取挑战赛已吸引来自中.美.英.法.德等26个国家和地区的2400余名选手参赛,目前仍在火热进行中(点击"阅读原文"进入比赛页 ...

  3. Online Learning算法理论与实践

    Online Learning是工业界比较常用的机器学习算法,在很多场景下都能有很好的效果.本文主要介绍Online Learning的基本原理和两种常用的Online Learning算法:FTRL ...

  4. 光线追踪(RayTracing)算法理论与实践(三)光照

    提要 经过之前的学习,我们已经可以在利用光线追踪实现一些简单的场景.今天我们要探讨的是图形学里面的三种基本光源:方向光源,点光源,聚光灯. 不同于利用现成的Api,这次会从理论到实际一步步用C++实现 ...

  5. Chapter 12 PCA与SVD算法理论与实践

    前言 ​ 最近,在学习机器学习算法时,看到了PCA(主成分分析)和SVD(奇异值分解),这是两个基本数据降维的算法,而在降维算法中的"降维"主要是指降低特征矩阵中特征数量的维度,直 ...

  6. 【视频课】模型优化拆分!分别学习模型剪枝与模型量化理论与实践

    前言 好的模型结构是深度学习成功的关键因素之一,不仅是非常重要的学术研究方向,在工业界实践中也是模型是否能上线的关键.对各类底层深度学习模型设计和优化技术理解的深度是决定我们能否在深度学习项目中游刃有 ...

  7. 【总结】最好的CV学习小组,超200个课时+10个方向+30个项目,从理论到实践全部系统掌握...

    2022年有三AI-CV夏季划已经正式发布,有三AI-CV夏季划是我们最系统最重要的深度学习和计算机视觉学习计划小组,目标是在新手入门的基础之上,彻底掌握好CV的重要方向,同时提升模型设计与优化的工程 ...

  8. 【视频课】行为识别课程更新!CNN+LSTM理论与实践!

    前言 欢迎大家关注有三AI的视频课程系列,我们的视频课程系列共分为5层境界,内容和学习路线图如下: 第1层:掌握学习算法必要的预备知识,包括Python编程,深度学习基础,数据使用,框架使用. 第2层 ...

  9. Java 理论与实践: 非阻塞算法简介——看吧,没有锁定!(转载)

    简介: Java™ 5.0 第一次让使用 Java 语言开发非阻塞算法成为可能,java.util.concurrent 包充分地利用了这个功能.非阻塞算法属于并发算法,它们可以安全地派生它们的线程, ...

  10. 相机激光标定算法:从理论到实践

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 本文是标定系列解读第三篇,介绍了Camera-Lidar标定,通过对一些基础知识和小细节进行讨论和理论 ...

最新文章

  1. JavaBean组件的基本使用-语法
  2. 基于matlab_simulink的捷联惯性导航系统仿真,基于MATLAB/Simulink的捷联惯性导航系统仿真...
  3. linux 内核网络协议栈--数据从接收到IP层(二)
  4. webuploader在bootstrap模态对话框中选择文件按钮无效的问题
  5. Django - app
  6. asp.net textbox 控件如何清除缓存
  7. java:蓝桥杯 矩阵乘法
  8. 客户端是选择Java Swing还是C# Winform
  9. PHP操作文件和目录的相关函数
  10. php面试php数组变ahp,关于PHP字符串的一道面试题
  11. 从Linux终端管理进程:10个你必须知道的命令
  12. 结构体在固件库中的应用
  13. JSON在Java中的使用(一)
  14. 论语之泰伯第八、子罕第九、乡党第十
  15. Oracle必读好书推荐
  16. vue m3u8格式 实现监控直播
  17. 在html中input是什么意思,在HTML代码中input是什么意思?
  18. JAVA求三角形的面积
  19. JVM基本原理,简明扼要,通俗易懂
  20. 科比自传读后感 --曼巴精神

热门文章

  1. 使用pdfobject.js实现在线浏览PDF--后台上传保存文件
  2. 前端框架bootstrap和可视化布局工具
  3. dsf5.0 element ui表单相关验证
  4. 计算机硬件安装装置,一种计算机硬件教学装置制造方法及图纸
  5. python爬虫大众点评_Python爬虫(三)爬取大众点评网
  6. 批处理变量输出时提示“ECHO处于关闭状态”
  7. 步进电机正反转实验_电机正反转控制电路图原理图解
  8. 引领智慧教育,联想云桌面如何打造教育“一朵云”?
  9. 2、Ubuntu下安装Vivado下的下载器驱动 Digilent 版本
  10. CGB2005 JT-4(聚合工程 阿里数据源,配置项目启动项,EasyUI,树形结构,页面跳转restFul,JSON串说明,vo po,分页查询,叶子类目,Ajax嵌套,windows端口号占用)