完整资源:

我的Github地址

前情提要:

从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍

目录

  • 前言
  • 阴影实现原理
  • P点到光源的距离
  • PO连线上离光源最近的实体点到光源的距离
    • shadow纹理
    • 获取深度信息
  • 绘制阴影
  • 着色器编写
    • compsite.vsh
    • conpsite.fsh
  • 小结

前言

在当了n天的懒狗之后,还是 掘腚 决定填一下坑。。今天来完成咱光影包的第一个特效,也是最基础的特效–阴影绘制

阴影实现原理

众所周知,光照到物体上会反射,而光被物体挡住,物体遮蔽的部分就会呈现暗色,这就是阴影。

如图所示:

dbq放错图了。。


已知一个点 P,光源为 O 点 ,如何判断 P 点是否落在阴影中呢?

这里有一个很简单的方法:比较 P点到光源的距离PO连线上离光源最近的实体点到光源的距离

假设

  1. P 点到光源的距离 = s1
  2. PO 连线上离光源最近的实体点到光源的距离 = s2

如果 s1 > s2 则说明 P 点在阴影中

如图:因为同一直线上,有别的点离光源更近,说明P点被覆盖,故 P 在阴影中。


P点在阴影中,那么我们直接将其涂黑就好了。难点在于如何获取P点到光源的距离

P点到光源的距离

已知一个点 P,光源为 O 点

已知 P 点,要看 P 点是否需要被涂黑,需要计算P点到光源的距离。

在 “太阳的屏幕坐标系” 下,z轴(即深度通道)为该点到太阳的距离。可是我们只知道 P 点在屏幕坐标系中的位置,而如何知道 P 点在 “太阳的屏幕坐标系” 下的位置?

好在 shadermod 提供了变换矩阵作为可用变量。下面给出四个可用的变换矩阵:

uniform mat4 shadowModelView;
uniform mat4 shadowModelViewInverse;
uniform mat4 shadowProjection;
uniform mat4 shadowProjectionInverse;

值得注意的是,太阳视角下的坐标系,并非opengl的默认坐标系,需要通过 “我的世界坐标” 转换而得到。

还记得 “我的世界坐标” 吗?上一篇博客提到过。即原点在摄像机处的世界坐标,原点随着摄像机移动而移动。

下面给出各种坐标和太阳视角下的坐标的转换关系:

#mermaid-svg-8NA24cxWaBpPicZ8 .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .label text{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .node rect,#mermaid-svg-8NA24cxWaBpPicZ8 .node circle,#mermaid-svg-8NA24cxWaBpPicZ8 .node ellipse,#mermaid-svg-8NA24cxWaBpPicZ8 .node polygon,#mermaid-svg-8NA24cxWaBpPicZ8 .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-8NA24cxWaBpPicZ8 .node .label{text-align:center;fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .node.clickable{cursor:pointer}#mermaid-svg-8NA24cxWaBpPicZ8 .arrowheadPath{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-8NA24cxWaBpPicZ8 .flowchart-link{stroke:#333;fill:none}#mermaid-svg-8NA24cxWaBpPicZ8 .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-8NA24cxWaBpPicZ8 .edgeLabel rect{opacity:0.9}#mermaid-svg-8NA24cxWaBpPicZ8 .edgeLabel span{color:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-8NA24cxWaBpPicZ8 .cluster text{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-8NA24cxWaBpPicZ8 .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-8NA24cxWaBpPicZ8 text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-8NA24cxWaBpPicZ8 .actor-line{stroke:grey}#mermaid-svg-8NA24cxWaBpPicZ8 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-8NA24cxWaBpPicZ8 #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .sequenceNumber{fill:#fff}#mermaid-svg-8NA24cxWaBpPicZ8 #sequencenumber{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 #crosshead path{fill:#333;stroke:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .messageText{fill:#333;stroke:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-8NA24cxWaBpPicZ8 .labelText,#mermaid-svg-8NA24cxWaBpPicZ8 .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-8NA24cxWaBpPicZ8 .loopText,#mermaid-svg-8NA24cxWaBpPicZ8 .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-8NA24cxWaBpPicZ8 .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-8NA24cxWaBpPicZ8 .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-8NA24cxWaBpPicZ8 .noteText,#mermaid-svg-8NA24cxWaBpPicZ8 .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-8NA24cxWaBpPicZ8 .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-8NA24cxWaBpPicZ8 .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-8NA24cxWaBpPicZ8 .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-8NA24cxWaBpPicZ8 .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 .section{stroke:none;opacity:0.2}#mermaid-svg-8NA24cxWaBpPicZ8 .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-8NA24cxWaBpPicZ8 .section2{fill:#fff400}#mermaid-svg-8NA24cxWaBpPicZ8 .section1,#mermaid-svg-8NA24cxWaBpPicZ8 .section3{fill:#fff;opacity:0.2}#mermaid-svg-8NA24cxWaBpPicZ8 .sectionTitle0{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .sectionTitle1{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .sectionTitle2{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .sectionTitle3{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-8NA24cxWaBpPicZ8 .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 .grid path{stroke-width:0}#mermaid-svg-8NA24cxWaBpPicZ8 .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-8NA24cxWaBpPicZ8 .task{stroke-width:2}#mermaid-svg-8NA24cxWaBpPicZ8 .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 .taskText:not([font-size]){font-size:11px}#mermaid-svg-8NA24cxWaBpPicZ8 .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-8NA24cxWaBpPicZ8 .task.clickable{cursor:pointer}#mermaid-svg-8NA24cxWaBpPicZ8 .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-8NA24cxWaBpPicZ8 .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-8NA24cxWaBpPicZ8 .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-8NA24cxWaBpPicZ8 .taskText0,#mermaid-svg-8NA24cxWaBpPicZ8 .taskText1,#mermaid-svg-8NA24cxWaBpPicZ8 .taskText2,#mermaid-svg-8NA24cxWaBpPicZ8 .taskText3{fill:#fff}#mermaid-svg-8NA24cxWaBpPicZ8 .task0,#mermaid-svg-8NA24cxWaBpPicZ8 .task1,#mermaid-svg-8NA24cxWaBpPicZ8 .task2,#mermaid-svg-8NA24cxWaBpPicZ8 .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-8NA24cxWaBpPicZ8 .taskTextOutside0,#mermaid-svg-8NA24cxWaBpPicZ8 .taskTextOutside2{fill:#000}#mermaid-svg-8NA24cxWaBpPicZ8 .taskTextOutside1,#mermaid-svg-8NA24cxWaBpPicZ8 .taskTextOutside3{fill:#000}#mermaid-svg-8NA24cxWaBpPicZ8 .active0,#mermaid-svg-8NA24cxWaBpPicZ8 .active1,#mermaid-svg-8NA24cxWaBpPicZ8 .active2,#mermaid-svg-8NA24cxWaBpPicZ8 .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-8NA24cxWaBpPicZ8 .activeText0,#mermaid-svg-8NA24cxWaBpPicZ8 .activeText1,#mermaid-svg-8NA24cxWaBpPicZ8 .activeText2,#mermaid-svg-8NA24cxWaBpPicZ8 .activeText3{fill:#000 !important}#mermaid-svg-8NA24cxWaBpPicZ8 .done0,#mermaid-svg-8NA24cxWaBpPicZ8 .done1,#mermaid-svg-8NA24cxWaBpPicZ8 .done2,#mermaid-svg-8NA24cxWaBpPicZ8 .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-8NA24cxWaBpPicZ8 .doneText0,#mermaid-svg-8NA24cxWaBpPicZ8 .doneText1,#mermaid-svg-8NA24cxWaBpPicZ8 .doneText2,#mermaid-svg-8NA24cxWaBpPicZ8 .doneText3{fill:#000 !important}#mermaid-svg-8NA24cxWaBpPicZ8 .crit0,#mermaid-svg-8NA24cxWaBpPicZ8 .crit1,#mermaid-svg-8NA24cxWaBpPicZ8 .crit2,#mermaid-svg-8NA24cxWaBpPicZ8 .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-8NA24cxWaBpPicZ8 .activeCrit0,#mermaid-svg-8NA24cxWaBpPicZ8 .activeCrit1,#mermaid-svg-8NA24cxWaBpPicZ8 .activeCrit2,#mermaid-svg-8NA24cxWaBpPicZ8 .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-8NA24cxWaBpPicZ8 .doneCrit0,#mermaid-svg-8NA24cxWaBpPicZ8 .doneCrit1,#mermaid-svg-8NA24cxWaBpPicZ8 .doneCrit2,#mermaid-svg-8NA24cxWaBpPicZ8 .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-8NA24cxWaBpPicZ8 .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-8NA24cxWaBpPicZ8 .milestoneText{font-style:italic}#mermaid-svg-8NA24cxWaBpPicZ8 .doneCritText0,#mermaid-svg-8NA24cxWaBpPicZ8 .doneCritText1,#mermaid-svg-8NA24cxWaBpPicZ8 .doneCritText2,#mermaid-svg-8NA24cxWaBpPicZ8 .doneCritText3{fill:#000 !important}#mermaid-svg-8NA24cxWaBpPicZ8 .activeCritText0,#mermaid-svg-8NA24cxWaBpPicZ8 .activeCritText1,#mermaid-svg-8NA24cxWaBpPicZ8 .activeCritText2,#mermaid-svg-8NA24cxWaBpPicZ8 .activeCritText3{fill:#000 !important}#mermaid-svg-8NA24cxWaBpPicZ8 .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-8NA24cxWaBpPicZ8 g.classGroup text .title{font-weight:bolder}#mermaid-svg-8NA24cxWaBpPicZ8 g.clickable{cursor:pointer}#mermaid-svg-8NA24cxWaBpPicZ8 g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-8NA24cxWaBpPicZ8 g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-8NA24cxWaBpPicZ8 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-8NA24cxWaBpPicZ8 .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-8NA24cxWaBpPicZ8 .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-8NA24cxWaBpPicZ8 .dashed-line{stroke-dasharray:3}#mermaid-svg-8NA24cxWaBpPicZ8 #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8NA24cxWaBpPicZ8 #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8NA24cxWaBpPicZ8 #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-8NA24cxWaBpPicZ8 #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-8NA24cxWaBpPicZ8 #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8NA24cxWaBpPicZ8 #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8NA24cxWaBpPicZ8 #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8NA24cxWaBpPicZ8 #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8NA24cxWaBpPicZ8 .commit-id,#mermaid-svg-8NA24cxWaBpPicZ8 .commit-msg,#mermaid-svg-8NA24cxWaBpPicZ8 .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-8NA24cxWaBpPicZ8 g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-8NA24cxWaBpPicZ8 g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-8NA24cxWaBpPicZ8 g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-8NA24cxWaBpPicZ8 .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-8NA24cxWaBpPicZ8 .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-8NA24cxWaBpPicZ8 .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-8NA24cxWaBpPicZ8 .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-8NA24cxWaBpPicZ8 .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-8NA24cxWaBpPicZ8 .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-8NA24cxWaBpPicZ8 .edgeLabel text{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8NA24cxWaBpPicZ8 .node circle.state-start{fill:black;stroke:black}#mermaid-svg-8NA24cxWaBpPicZ8 .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-8NA24cxWaBpPicZ8 #statediagram-barbEnd{fill:#9370db}#mermaid-svg-8NA24cxWaBpPicZ8 .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-8NA24cxWaBpPicZ8 .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-8NA24cxWaBpPicZ8 .statediagram-state .divider{stroke:#9370db}#mermaid-svg-8NA24cxWaBpPicZ8 .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-8NA24cxWaBpPicZ8 .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-8NA24cxWaBpPicZ8 .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-8NA24cxWaBpPicZ8 .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-8NA24cxWaBpPicZ8 .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-8NA24cxWaBpPicZ8 .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-8NA24cxWaBpPicZ8 .note-edge{stroke-dasharray:5}#mermaid-svg-8NA24cxWaBpPicZ8 .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-8NA24cxWaBpPicZ8 .error-icon{fill:#522}#mermaid-svg-8NA24cxWaBpPicZ8 .error-text{fill:#522;stroke:#522}#mermaid-svg-8NA24cxWaBpPicZ8 .edge-thickness-normal{stroke-width:2px}#mermaid-svg-8NA24cxWaBpPicZ8 .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-8NA24cxWaBpPicZ8 .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-8NA24cxWaBpPicZ8 .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-8NA24cxWaBpPicZ8 .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-8NA24cxWaBpPicZ8 .marker{fill:#333}#mermaid-svg-8NA24cxWaBpPicZ8 .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}#mermaid-svg-8NA24cxWaBpPicZ8 {color: rgba(0, 0, 0, 0.75);font: ;}

乘 gbufferProjectionInverse 后 进行透视除法
乘 gbufferProjection 后 进行透视除法
乘 gbufferModelViewInverse
乘 gbufferModelView
乘 shadowModelView
乘 shadowProjection 后 进行透视除法
视口变换 乘0.5后加0.5
ndc坐标
眼坐标
我的世界坐标
太阳视角下的眼坐标
太阳视角下的ndc坐标
太阳视角下的屏幕坐标

下面给出 “我的世界坐标” 转太阳视角下的屏幕坐标的代码:

// P点的 我的世界坐标 注意不是世界坐标。。
vec4 positionInWorldCoord;  // 我的世界坐标 转 太阳的眼坐标
vec4 positionInSunViewCoord = shadowModelView * positionInWorldCoord;// 太阳的眼坐标 转 太阳的裁剪坐标
vec4 positionInSunClipCoord = shadowProjection * positionInSunViewCoord;// 太阳的裁剪坐标 转 太阳的ndc坐标
vec4 positionInSunNdcCoord = vec4(positionInSunClipCoord.xyz/positionInSunClipCoord.w, 1.0);// 太阳的ndc坐标 转 太阳的屏幕坐标
vec4 positionInSunScreenCoord = positionInSunNdcCoord * 0.5 + 0.5;

根据太阳视角下的屏幕坐标的 z 轴,即是 P 点距离太阳(光源)的距离,我们管他叫做 当前距离 currentDepth。

float currentDepth = positionInSunScreenCoord.z;    // 当前点的深度

解决了P点到光源的距离,还需要计算PO连线上离光源最近的实体点到光源的距离。下面介绍该方法。

PO连线上离光源最近的实体点到光源的距离

已知一个点 P,光源为 O 点

为了计算PO连线上离光源最近的实体点到光源的距离,这就需要引入 shadow 纹理了。

shadow纹理

shadow纹理是光影mod预先绘制的一张纹理图片,我们可以直接使用texcoord2D()查询纹理,而无需对其做任何其他处理。

和深度图类似,shadow纹理存储的数据也是深度,这个深度它描述了 离光源最近的实体点到光源的距离,而且是以光源的视角进行描述的。听起来很抽象,我们来看这样一张图片:

shadow纹理绘制的图片,是以太阳的视角出发,图片的像素值为 离光源最近的实体点到光源的距离。可以看到,近处的山体呈现暗色,说明离太阳距离近,而远处的山体呈现亮色,说明距离远。

注:右图是使用 vec4 color = texture2D(shadow, texcoord.st); 直接采样并且输出的shadow纹理的数据

获取深度信息

因为shadow纹理绘制的深度信息是以太阳的视角出发,我们需要使用 P 点在太阳视角下的屏幕坐标 对shadow纹理进行采样,才能够正确的得到深度的值。

 // 太阳视角下的屏幕坐标
vec4 positionInSunScreenCoord;// 查询纹理,获取 离光源最近的点的深度
float closest = texture2D(shadow, positionInSunScreenCoord.xy).x;

注:对shadow纹理的采样,和第一篇博客中提到的深度纹理的采样无异。只是本次采样需要在太阳视角(或者称之为光照坐标系。。)下进行

绘制阴影

已知一个点 P,光源为 O 点

通过上述的步骤,我们获得了

  1. P点到光源的距离(当前点深度) currentDepth
  2. PO连线上离光源最近的实体点到光源的距离 closest

之后的步骤非常简单了,如果当前点P的深度大于最近点的深度,说明该点在阴影中。我们通过降低颜色的RGB值来涂黑像素

// 如果当前点深度大于光照图中最近的点的深度 说明当前点在阴影中
if(closest <= currentDepth) {color.rgb *= 0.5;   // 涂黑
}

着色器编写

编写compsite.fsh 和 vsh

compsite.vsh

在顶点着色器中,完成对裁剪坐标的赋值

#version 120varying vec4 texcoord;void main() {// 为归一化的裁剪空间坐标赋值gl_Position = ftransform();  // 得到当前坐标在0号纹理(即输入图像)上的坐标texcoord = gl_TextureMatrix[0] * gl_MultiTexCoord0;
}

conpsite.fsh

在片段着色器中,我们编写一个函数,叫做 getShadow。

因为根据上文的分析,我们得到 “我的世界坐标”,就可以通过 shadowModelView 和 shadowProjection 坐标变换矩阵,得到 “太阳视角下的坐标”,进而计算出该点是否需要涂黑

所以 getShadow 的入口参数有两个:

  1. 当前像素颜色
  2. 当前像素在“我的世界坐标系”下的坐标

此外,getShadow 返回当前像素的新颜色,即涂黑或者不涂黑。

#version 120uniform sampler2D texture;
uniform sampler2D depthtex0;
uniform sampler2D shadow;uniform float far;uniform mat4 gbufferModelView;
uniform mat4 gbufferModelViewInverse;
uniform mat4 gbufferProjection;
uniform mat4 gbufferProjectionInverse;uniform mat4 shadowModelView;
uniform mat4 shadowModelViewInverse;
uniform mat4 shadowProjection;
uniform mat4 shadowProjectionInverse;varying vec4 texcoord;/** @function getShadow         : getShadow 渲染阴影* @param color                : 原始颜色* @param positionInWorldCoord : 该点在世界坐标系下的坐标* @return                     : 渲染阴影之后的颜色*/
vec4 getShadow(vec4 color, vec4 positionInWorldCoord) {// 我的世界坐标 转 太阳的眼坐标vec4 positionInSunViewCoord = shadowModelView * positionInWorldCoord;// 太阳的眼坐标 转 太阳的裁剪坐标vec4 positionInSunClipCoord = shadowProjection * positionInSunViewCoord;// 太阳的裁剪坐标 转 太阳的ndc坐标vec4 positionInSunNdcCoord = vec4(positionInSunClipCoord.xyz/positionInSunClipCoord.w, 1.0);// 太阳的ndc坐标 转 太阳的屏幕坐标vec4 positionInSunScreenCoord = positionInSunNdcCoord * 0.5 + 0.5;float currentDepth = positionInSunScreenCoord.z;    // 当前点的深度float closest = texture2D(shadow, positionInSunScreenCoord.xy).x;   // 离光源最近的点的深度// 如果当前点深度大于光照图中最近的点的深度 说明当前点在阴影中if(closest <= currentDepth) {color.rgb *= 0.5;   // 涂黑}return color;
}/* DRAWBUFFERS: 0 */
void main() {vec4 color = texture2D(texture, texcoord.st);float depth = texture2D(depthtex0, texcoord.st).x;// 利用深度缓冲建立带深度的ndc坐标vec4 positionInNdcCoord = vec4(texcoord.st*2-1, depth*2-1, 1);// 逆投影变换 -- ndc坐标转到裁剪坐标vec4 positionInClipCoord = gbufferProjectionInverse * positionInNdcCoord;// 透视除法 -- 裁剪坐标转到眼坐标vec4 positionInViewCoord = vec4(positionInClipCoord.xyz/positionInClipCoord.w, 1.0);// 逆 “视图模型” 变换 -- 眼坐标转 “我的世界坐标” vec4 positionInWorldCoord = gbufferModelViewInverse * positionInViewCoord;color = getShadow(color, positionInWorldCoord);gl_FragData[0] = color;
}

保存并且运行,你会看到:


许多错乱的阴影条纹,是因为 P 点经过坐标变换到太阳视角下的坐标系,因为浮点精度不足加上浮点的舍入,难免会有失真。

简单的通过 z 轴shadow纹理中的数据值 比对,对于不在阴影中的点,理论上说,他们数值是一样的,但是因为坐标变换导致的误差,有可能造成误判,解决方案就是在判断条件加一个小的偏移量即可:

现在,阴影能够被正常绘制了。。

小结

这一篇终于完成了第一个便宜特效的绘制。。摸了

可以看到咱的阴影还是有很多不足,这些不足会在下一章节被修复 摸了 ,比如分辨率堪忧,阴影的细节不够还原,史蒂夫都被扭曲成一坨翔了。。。


或者是太阳直射的时候,会因为采样精度不足,产生奇怪的错误绘制:


总之。。。算了 咕了

从零开始编写minecraft光影包(1)基础阴影绘制相关推荐

  1. 从零开始编写minecraft光影包(8)中级水面绘制 水下阴影与焦散

    完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...

  2. 从零开始编写minecraft光影包(6)天空绘制

    完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...

  3. 从零开始编写minecraft光影包(9)高级水面绘制 反射与屏幕空间反射

    完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...

  4. 从零开始编写minecraft光影包(5)简单光照系统,曝光调节,色调映射与饱和度

    完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...

  5. 从零开始编写minecraft光影包(4)泛光性能与品质优化

    完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...

  6. 我的世界光影mod怎么用_大片的正确打开方式-MineCraft光影材质包安装及使用教程...

    我从2014年开始玩MC到现在已经断断续续玩了1年了,这款像素方块游戏看起来不起眼,那是魅力深藏不露,一款能玩1年多的游戏,不但没有疲倦,而且周围跟着一起玩的人越来越多,可见这款游戏在某种程度已经激发 ...

  7. 从零开始编写网络游戏--基础篇(1)

    最近2周比较忙,没有抽出时间来写Blog,不过在这段时间里面把整个思路理了一遍,梳理了一下大纲,以后会多抽时间来写Blog. 好了,言归正传,做任何事情都需要一定的基础,没有坚实的地基,是不可能建立雄 ...

  8. 从零开始编写一个上位机(串口助手)QT Creator + Python

    提示:本博客作为学习笔记,有错误的地方希望指正,此文可能会比较长,作为学习笔记的积累,希望对来着有帮助.   绪论:笔者这里使用的是QTCreator和Python来实现一个简单的串口上位机的开发的简 ...

  9. 从零开始编写自己的C#框架(16)——Web层后端父类

    从零开始编写自己的C#框架(16)--Web层后端父类 原文:从零开始编写自己的C#框架(16)--Web层后端父类 本章节讲述的各个类是后端系统的核心之一,涉及到系统安全验证.操作日志记录.页面与按 ...

  10. 从零开始编写一个vue插件

    title: 从零开始编写一个vue插件 toc: true date: 2018-12-17 10:54:29 categories: Web tags: vue mathjax 写毕设的时候需要一 ...

最新文章

  1. aitken插值方法的c++代码_无人驾驶路径规划技术-三次样条插值曲线及Python代码实现...
  2. nginx+fastcgi+c/c++搭建高性能Web框架
  3. 项目整体管理:制定项目管理计划
  4. date比较大小 mybatis_MyBatis Sqlserver日期比较
  5. 搭载恩智浦i.MX 8M Plus处理器的核心板,它来了!
  6. sql缩进提高语句的可读性_为什么要使用列缩进来提高代码的可读性
  7. JavaSE的输入流、输出流
  8. leetcode 273场周赛 Problem-C
  9. ABC三类地址及其子网掩码
  10. Arduino Uno+步进电机28BYJ-48+ULN2003 实现简单的正反转demo
  11. 后端自我介绍_java开发自我介绍3篇
  12. 【通知】4月18日22:00至4月19日2:00网站服务暂停公告
  13. MSP430下载程序BSL
  14. Excel常用技巧(一)
  15. 微信单删和互删有什么区别?
  16. python数据分列_Python pandas 数据无法正常分列
  17. 侬用洲的移动通信复习资料
  18. Spark Sql 聚合
  19. 均值、方差和标准差计算的python代码
  20. tplink android管理软件,tplink手机app下载

热门文章

  1. 三顾茅庐:从刘备面试孔明得出的选股经验
  2. window.open,打开窗口与打开新标签页,刷新父窗口数据
  3. 北航计算机专业最低分,北京航空航天大学2020年本科录取线发布,最低分572
  4. android 获取alertdialog的view,Android开发实现AlertDialog中View的控件设置监听功能分析...
  5. java编程第七周作业
  6. 【渝粤题库】广东开放大学 发展与教育心理学 形成性考核
  7. 来自腾讯的高性能服务器架构思路
  8. 成功GET一款高大上又不显俗的Linux时间锁屏软件-GLUQLO
  9. 计算机cccc比赛,2019第四届中国高校计算机大赛—团体程序设计天梯赛 CCCC 总结...
  10. 毕索大学计算机科学怎么样,毕索大学与麦吉尔大学哪个好