三维引擎设计专题--大气散射特效

在做GIS时, 地球周围会有一个大气圈, 大气散射, 这个方面的算法是计算机图形学界不断深入研究的领域, 不过目前有几个成熟的散射算法. 我借鉴了<<GPU精粹2.高性能图形芯片和通用计算机编程技巧>>第16章的算法,实现了一个大气散射. 效果如图.

图中蓝色的天空,就是散射的效果, 具体算法请自行查看书上的算法吧.

步骤:

1: 创建一个椭球, 生成顶点,与顶点索引数组.  这个椭球生成算法后续贴出来.

2: 根据算法传递uniform, 运行shader

3:关于影像,高程的处理,后续贴出.

shader源代码:

顶点:

uniform vec3 u_viewerPositionWC;

uniform vec3 u_sunDirectionWC;

const float u_pi = 3.141592653589793;

uniform mat4 u_modelViewProjection;

uniform mat4 u_modelView;

#define SKY_FROM_ATMOSPHERE

attribute vec4 position;

uniform float fCameraHeight;

uniform float fCameraHeight2;

uniform float fOuterRadius;     // The outer (atmosphere) radius

uniform float fOuterRadius2;    // fOuterRadius^2

uniform float fInnerRadius;     // The inner (planetary) radius

uniform float fScale;           // 1 / (fOuterRadius - fInnerRadius)

uniform float fScaleDepth;      // The scale depth (i.e. the altitude at which the atmosphere‘s average density is found)

uniform float fScaleOverScaleDepth; // fScale / fScaleDepth

const float Kr = 0.0025;

const float fKr4PI = Kr * 4.0 * u_pi;

const float Km = 0.0015;

const float fKm4PI = Km * 4.0 * u_pi;

const float ESun = 15.0;

const float fKmESun = Km * ESun;

const float fKrESun = Kr * ESun;

const vec3 v3InvWavelength = vec3(

5.60204474633241,  // Red = 1.0 / Math.pow(0.650, 4.0)

9.473284437923038, // Green = 1.0 / Math.pow(0.570, 4.0)

19.643802610477206); // Blue = 1.0 / Math.pow(0.475, 4.0)

const float rayleighScaleDepth = 0.25;

const int nSamples = 2;

const float fSamples = 2.0;

varying vec3 v_rayleighColor;

varying vec3 v_mieColor;

varying vec3 v_toCamera;

varying vec3 v_positionEC;

float scale(float fCos)

{

float x = 1.0 - fCos;

return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));

}

void main(void)

{

// Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)

vec3 v3Pos = position.xyz;

vec3 v3Ray = v3Pos - u_viewerPositionWC;

float fFar = length(v3Ray);

v3Ray /= fFar;

#ifdef SKY_FROM_SPACE

// Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere)

float B = 2.0 * dot(u_viewerPositionWC, v3Ray);

float C = fCameraHeight2 - fOuterRadius2;

float fDet = max(0.0, B*B - 4.0 * C);

float fNear = 0.5 * (-B - sqrt(fDet));

// Calculate the ray‘s starting position, then calculate its scattering offset

vec3 v3Start = u_viewerPositionWC + v3Ray * fNear;

fFar -= fNear;

float fStartAngle = dot(v3Ray, v3Start) / fOuterRadius;

float fStartDepth = exp(-1.0 / fScaleDepth);

float fStartOffset = fStartDepth*scale(fStartAngle);

#else // SKY_FROM_ATMOSPHERE

// Calculate the ray‘s starting position, then calculate its scattering offset

vec3 v3Start = u_viewerPositionWC;

float fHeight = length(v3Start);

float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fCameraHeight));

float fStartAngle = dot(v3Ray, v3Start) / fHeight;

float fStartOffset = fDepth*scale(fStartAngle);

#endif

// Initialize the scattering loop variables

float fSampleLength = fFar / fSamples;

float fScaledLength = fSampleLength * fScale;

vec3 v3SampleRay = v3Ray * fSampleLength;

vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;

// Now loop through the sample rays

vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);

for(int i=0; i<nSamples; i++)

{

float fHeight = length(v3SamplePoint);

float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));

vec3 lightPosition = normalize(u_viewerPositionWC); // u_sunDirectionWC

float fLightAngle = dot(lightPosition, v3SamplePoint) / fHeight;

float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight;

float fScatter = (fStartOffset + fDepth*(scale(fLightAngle) - scale(fCameraAngle)));

vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));

v3FrontColor += v3Attenuate * (fDepth * fScaledLength);

v3SamplePoint += v3SampleRay;

}

// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader

v_mieColor = v3FrontColor * fKmESun;

v_rayleighColor = v3FrontColor * (v3InvWavelength * fKrESun);

v_toCamera = u_viewerPositionWC - v3Pos;

v_positionEC = (u_modelView * position).xyz;

gl_Position = u_modelViewProjection * position;

}

片段:

#ifdef GL_FRAGMENT_PRECISION_HIGH

precision highp float;

#else

precision mediump float;

#endif

const float u_infinity = 5906376272000.0;

struct u_raySegment

{

float start;

float stop;

};

const u_raySegment u_emptyRaySegment = u_raySegment(-u_infinity, -u_infinity);

const u_raySegment u_fullRaySegment = u_raySegment(0.0, u_infinity);

struct u_ray

{

vec3 origin;

vec3 direction;

};

uniform mat4 u_inverseModelView;

struct u_ellipsoid

{

vec3 center;

vec3 radii;

vec3 inverseRadii;

vec3 inverseRadiiSquared;

};

uniform mat4 u_view;

uniform vec3 u_sunDirectionWC;

u_raySegment u_rayEllipsoidIntersectionInterval(u_ray ray, u_ellipsoid ellipsoid)

{

// ray and ellipsoid center in eye coordinates.  radii in model coordinates.

vec3 q = ellipsoid.inverseRadii * (u_inverseModelView * vec4(ray.origin, 1.0)).xyz;

vec3 w = ellipsoid.inverseRadii * (u_inverseModelView * vec4(ray.direction, 0.0)).xyz;

q = q - ellipsoid.inverseRadii * (u_inverseModelView * vec4(ellipsoid.center, 1.0)).xyz;

float q2 = dot(q, q);

float qw = dot(q, w);

if (q2 > 1.0) // Outside ellipsoid.

{

if (qw >= 0.0) // Looking outward or tangent (0 intersections).

{

return u_emptyRaySegment;

}

else // qw < 0.0.

{

float qw2 = qw * qw;

float difference = q2 - 1.0; // Positively valued.

float w2 = dot(w, w);

float product = w2 * difference;

if (qw2 < product) // Imaginary roots (0 intersections).

{

return u_emptyRaySegment;

}

else if (qw2 > product) // Distinct roots (2 intersections).

{

float discriminant = qw * qw - product;

float temp = -qw + sqrt(discriminant); // Avoid cancellation.

float root0 = temp / w2;

float root1 = difference / temp;

if (root0 < root1)

{

u_raySegment i = u_raySegment(root0, root1);

return i;

}

else

{

u_raySegment i = u_raySegment(root1, root0);

return i;

}

}

else // qw2 == product.  Repeated roots (2 intersections).

{

float root = sqrt(difference / w2);

u_raySegment i = u_raySegment(root, root);

return i;

}

}

}

else if (q2 < 1.0) // Inside ellipsoid (2 intersections).

{

float difference = q2 - 1.0; // Negatively valued.

float w2 = dot(w, w);

float product = w2 * difference; // Negatively valued.

float discriminant = qw * qw - product;

float temp = -qw + sqrt(discriminant); // Positively valued.

u_raySegment i = u_raySegment(0.0, temp / w2);

return i;

}

else // q2 == 1.0. On ellipsoid.

{

if (qw < 0.0) // Looking inward.

{

float w2 = dot(w, w);

u_raySegment i = u_raySegment(0.0, -qw / w2);

return i;

}

else // qw >= 0.0.  Looking outward or tangent.

{

return u_emptyRaySegment;

}

}

}

uniform float u_morphTime;

float u_luminance(vec3 rgb)

{

// Algorithm from Chapter 10 of Graphics Shaders.

const vec3 W = vec3(0.2125, 0.7154, 0.0721);

return dot(rgb, W);

}

bool u_isEmpty(u_raySegment interval)

{

return (interval.stop < 0.0);

}

u_ellipsoid u_getWgs84EllipsoidEC()

{

vec3 radii = vec3(6378137.0, 6378137.0, 6356752.314245);

vec3 inverseRadii = vec3(1.0 / radii.x, 1.0 / radii.y, 1.0 / radii.z);

vec3 inverseRadiiSquared = inverseRadii * inverseRadii;

u_ellipsoid temp = u_ellipsoid(u_view[3].xyz, radii, inverseRadii, inverseRadiiSquared);

return temp;

}

const float g = -0.95;

const float g2 = g * g;

varying vec3 v_rayleighColor;

varying vec3 v_mieColor;

varying vec3 v_toCamera;

varying vec3 v_positionEC;

void main (void)

{

// TODO: make arbitrary ellipsoid

u_ellipsoid ellipsoid = u_getWgs84EllipsoidEC();

vec3 direction = normalize(v_positionEC);

u_ray ray = u_ray(vec3(0.0), direction);

u_raySegment intersection = u_rayEllipsoidIntersectionInterval(ray, ellipsoid);

if (!u_isEmpty(intersection)) {

discard;

}

// Extra normalize added for Android

float fCos = dot(u_sunDirectionWC, normalize(v_toCamera)) / length(v_toCamera);

float fRayleighPhase = 0.75 * (1.0 + fCos*fCos);

float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);

const float fExposure = 2.0;

vec3 rgb = fRayleighPhase * v_rayleighColor + fMiePhase * v_mieColor;

rgb = vec3(1.0) - exp(-fExposure * rgb);

float l = u_luminance(rgb);

gl_FragColor = vec4(rgb, min(smoothstep(0.0, 0.1, l), 1.0) * smoothstep(0.0, 1.0, u_morphTime));

}

时间: 2024-08-28 20:26:24

三维引擎设计专题--大气散射特效的相关文章

三维引擎设计专题--文字的渲染

第一部分: 基础概念: 1: 先想想文字在计算机内怎么表示, 经过前辈们的努力, 有两种表示方法, 一种是点阵字体, 即二维数组, 表示字体像素信息, 点阵字体缺点是很难进行缩放, 常见的格式有bdf,pcf,fnt,hbf. 另外一种是矢量字体, 一个文字通过数学曲线来描述, 字体渲染引擎解析这些矢量数据, 常见的格式有type1, treeType, freeType. 2: 其次,字体在计算机内存储格式已经定义好了, 想想字体存储与解析这么基础的东西, 肯定已经成为操作系统的一部分了. 对

三维引擎设计-图形窗口封装

一:概述: 每个操作系统都有自己的图形系统,三维引擎会抽象出一个窗口,然后通过继承的方式,子类分别封装不同平台下面的窗口,另外,三维图形API也支持将内容渲染到其他表面上,比如纹理中,所以三维引擎也会抽象出一个纹理,再通过继承的方式,由子类分别封装不同图形API的纹理. 窗口和纹理,都可以看成一个抽象的画布,接收三维引擎的结果渲染到这个画布上,总体来说,一个抽象的画布,代表抽象的窗口或纹理,不同平台和图形API下的窗口和纹理又子类实现.这是三维引擎封装窗口系统的一种方法. 二:OSG的设计: 1

三维引擎设计-渲染层封装(Osg渲染层结构)

OSG渲染层封装 OpenGL_API可以大致分成这样几个部分: 1:顶点操作(立即模式,显示列表,顶点数组,顶点缓冲区), 2:纹理(一维纹理,二维纹理,三维纹理,天空盒), 3:灯光(类型,光源,光照模型) , 4:着色器(顶点着色器,片段着色器,一致变量,属性变量), 5:帧缓冲区(渲染缓冲区), 6:状态(Alpha校验,Blending混合,模板测试,裁剪测试,深度测试,雾,光栅化设置), 7:API扩展管理等. Osg封装了上述大部分操作.由于OpenGL是个状态机,上述API操作可

三维引擎设计-多线程渲染(平台API基础和封装大致框架)

第一部分: Linux线程API基础 一:线程创建与结束 (1)pthread_t //线程的标识符类型 (2)pthread_create //用来创建一个线程, 参数线程标识符, 线程属性, 线程运行函数地址 (3)pthread_join //用来等待一个线程的结束, 参数被等待线程标识符,用户自定义指针 (4)pthread_exit //线程非正常结束,参数线程返回代码 二:修改线程属性 (1)pthread_attr_t //线程属性结构类型 (2)pthread_attr_init

三维动画设计及应用

在今天,三维动画设计,在虚拟现实应用中已成为一种流行文化,该技术的出现为各行业提供了很多的帮助.之前,素描本上的工作被搬到了计算机上,工业设计和工程建设不再用真实的模型,都可以在电脑上看到他们做成后的效果图,这样就节省了很多的成本,其中,三维动画设计还应用在多个行业的快速发展中,也为很多行业提供了出色的作品. 1.三维动画设计应用在游戏动画 三维动画设计的软件主要是有EA.Epic.SEGA等,她大量应用于游戏的场景设计.角色造型建模和角色动作的设计,以及游戏动画的特效声音场景的制作. 2.三维

VC++实战《星际传奇》网游课程第一部分网络游戏开发基础篇(游戏引擎设计)

本系列课程基于最新的DirectX11接口进行深入细致的讲解,内容涉及D3D11原理与应用.DirectInput.DirectSound等: 教程中专门针对新兴的D3D11接口展开深入的讲解,详细讲解了D3D11渲染管线.DirectComputer(参看<VC++游戏开发系列之Directcomputer并行计算原理与实践--DX11游戏实战开发>).Tessellation.多线程渲染.Shader动态链接等新内容.新知识.并且基于这些内容的基础,更进一步讲解了光照模型原理及实现.高级的

微型工作流引擎设计

微型工作流引擎设计 一.前言 提到工作流很多人就会想到OA,的确OA就是典型的工作流的应用,但是工作流并不仅仅局限于OA,工作流应该算是基础框架软件,主要用于流程的重组和优化,它有广阔的应用领域.在java下有很多优秀的开源工作流可以选择比如activit5.jpbm4等,在.net下却几乎找不到令人满意的工作流引擎可用.当然不是说.net下没有开源的只是有些国产开源的但看了代码后就一点兴趣都没有了,且不说代码质量如何,还引入了一大堆的东西,想在项目中应用也是非常困难.鉴于此我还是决定自己开发一

OpenSceneGraph是一个开源的三维引擎

http://www.osgchina.org/OpenSceneGraph是一个开源的三维引擎,被广泛的应用在可视化仿真.游戏.虚拟现实.科学计算.三维重建.地理信息.太空探索.石油矿产等领域.OSG采用标准C++和OpenGL编写而成,可运行在所有的Windows平台.OSX.GNU/Linux.IRIX.Solaris.HP-Ux.AIX.Android和FreeBSD 操作系统.OSG在各个行业均有着丰富的扩展,能够与使用OpenGL书写的引擎无缝的结合,使用国际上最先进的图形渲染技术,

引擎设计跟踪(九.14.2a) 导出插件问题修复和 Tangent Space 裂缝修复

由于工作很忙, 近半年的业余时间没空搞了, 不过工作马上忙完了, 趁十一有时间修了一些小问题. 这次更新跟骨骼动画无关, 修复了一个之前的, 关于tangent space裂缝的问题: 引擎设计跟踪(九) 3DS MAX 导出插件 引擎设计跟踪(九.10) Max插件更新,地形问题备忘 这里说明一下修复方法, 并且做一个总结. 之前的做法都不算错, 但是不完善. 这里有缝, 主要是因为那个战争机器3的模型本身已经复制了顶点( 左半部分和右半部分是不同的mesh, 有重合的顶点), 接缝处的顶点虽