facing3d subsets an object by converting it to a triangle mesh, then subsetting to those triangles that are counterclockwise (for front = TRUE) when projected into a plane.

projectDown computes a projection that “looks down” the specified direction.

facing3d(obj, up = c(0, 0, 1),
P = projectDown(up),
front = TRUE, strict = TRUE)
projectDown(up)

## Arguments

obj

An object that can be converted to a triangular mesh object.

up

The direction that is to be considered “up”. It may be either a 3 vector in Euclidean coordinates or a 4 vector in homogeneous coordinates.

P

The projection to use for draping, a 4x4 matrix. See drape3d for details on how P is used.

front

If front = TRUE, retains triangles that are counterclockwise after projection by P, otherwise retains those that are clockwise.

strict

If TRUE, drops indeterminate triangles (those that are annihilated by P).

## Details

By default the returned subset will be those triangles whose upper side matches front. Change up or use an arbitrary projection for different subsets.

drape3d and shadow3d project objects onto meshes; these functions can be used to project only onto the top or front.

## Value

facing3d returns a mesh object made of those triangles which face in the desired direction.

projectDown computes a 4x4 matrix. The first two coordinates of asEuclidean(x %*% projectDown(up))

give a projection of x from above into a plane, where up determines which direction is taken to be “up”.

drape3d, shadow3d

## Examples

open3d()
d <- rnorm(3)
d <- d/sqrt(sum(d^2))
shade3d( facing3d( icosahedron3d(), up = d, strict = FALSE),
col = "yellow")
wire3d( facing3d( icosahedron3d(), up = d, front = FALSE),
col = "black")
# Show the direction:
arrow3d(-2*d , -d)
3D plot

{"x":{"material":{"color":"#000000","alpha":1,"lit":true,"ambient":"#000000","specular":"#FFFFFF","emission":"#000000","shininess":50,"smooth":true,"front":"filled","back":"filled","size":3,"lwd":1,"fog":true,"point_antialias":false,"line_antialias":false,"texture":null,"textype":"rgb","texmode":"modulate","texmipmap":false,"texminfilter":"linear","texmagfilter":"linear","texenvmap":false,"depth_mask":true,"depth_test":"less","isTransparent":false,"polygon_offset":[0,0],"margin":"","floating":false,"tag":"","blend":["src_alpha","one_minus_src_alpha"]},"rootSubscene":488,"objects":{"494":{"id":494,"type":"triangles","material":{},"vertices":"0","colors":"2","centers":"3","normals":"1","ignoreExtent":false,"flags":32771},"495":{"id":495,"type":"triangles","material":{"front":"lines","back":"lines"},"vertices":"4","colors":"6","centers":"7","normals":"5","ignoreExtent":false,"flags":32771},"496":{"id":496,"type":"triangles","material":{},"vertices":"8","colors":"10","centers":"11","normals":"9","ignoreExtent":false,"flags":32771},"497":{"id":497,"type":"quads","material":{},"vertices":"12","colors":"14","centers":"15","normals":"13","ignoreExtent":false,"flags":32771},"492":{"id":492,"type":"light","vertices":[[0,0,1]],"colors":[[1,1,1,1],[1,1,1,1],[1,1,1,1]],"viewpoint":true,"finite":false},"491":{"id":491,"type":"background","material":{},"colors":"16","centers":"17","sphere":false,"fogtype":"none","fogscale":1,"flags":32768},"493":{"id":493,"type":"background","material":{"lit":false,"back":"lines"},"colors":"18","centers":"19","sphere":false,"fogtype":"none","fogscale":1,"flags":32768},"488":{"id":488,"type":"subscene","par3d":{"antialias":8,"FOV":30,"ignoreExtent":false,"listeners":488,"mouseMode":{"none":"none","left":"trackball","right":"zoom","middle":"fov","wheel":"pull"},"observer":[0,0,7.667454719543457],"modelMatrix":[[1,0,0,0.1119049191474915],[0,0.3420201539993286,0.9396926164627075,-0.2861799001693726],[0,-0.9396926164627075,0.3420201539993286,-7.771615505218506],[0,0,0,1]],"projMatrix":[[3.732050657272339,0,0,0],[0,3.732050657272339,0,0],[0,0,-3.863703966140747,-27.64028930664062],[0,0,-1,0]],"skipRedraw":false,"userMatrix":[[1,0,0,0],[0,0.3420201433256682,0.9396926207859085,0],[0,-0.9396926207859085,0.3420201433256682,0],[0,0,0,1]],"userProjection":[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],"scale":[1,1,1],"viewport":{"x":0,"y":0,"width":1,"height":1},"zoom":1,"bbox":[-1.223809838294983,1,-1,1,-1,1.609092593193054],"windowRect":[540,585,796,841],"family":"sans","font":1,"cex":1,"useFreeType":true,"fontname":"/Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/library/rgl/fonts/FreeSans.ttf","maxClipPlanes":6,"glVersion":2.1,"activeSubscene":0},"embeddings":{"viewport":"replace","projection":"replace","model":"replace","mouse":"replace"},"objects":[493,494,495,496,497,492],"subscenes":[],"flags":33027}},"crosstalk":{"key":[],"group":[],"id":[],"options":[]},"width":480,"height":480,"buffer":{"accessors":[{"bufferView":0,"componentType":5126,"count":30,"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":30,"type":"VEC3"},{"bufferView":2,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":3,"componentType":5126,"count":10,"type":"VEC3"},{"bufferView":4,"componentType":5126,"count":30,"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":30,"type":"VEC3"},{"bufferView":6,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":7,"componentType":5126,"count":10,"type":"VEC3"},{"bufferView":8,"componentType":5126,"count":42,"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":42,"type":"VEC3"},{"bufferView":10,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":11,"componentType":5126,"count":14,"type":"VEC3"},{"bufferView":12,"componentType":5126,"count":36,"type":"VEC3"},{"bufferView":13,"componentType":5126,"count":36,"type":"VEC3"},{"bufferView":14,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":15,"componentType":5126,"count":9,"type":"VEC3"},{"bufferView":16,"componentType":5126,"count":1,"type":"VEC4"},{"bufferView":17,"componentType":5121,"count":1,"type":"VEC3"},{"bufferView":18,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":19,"componentType":5121,"count":1,"type":"VEC3"}],"bufferViews":[{"buffer":0,"byteLength":360,"byteOffset":0},{"buffer":0,"byteLength":360,"byteOffset":360},{"buffer":0,"byteLength":4,"byteOffset":720},{"buffer":0,"byteLength":120,"byteOffset":724},{"buffer":0,"byteLength":360,"byteOffset":844},{"buffer":0,"byteLength":360,"byteOffset":1204},{"buffer":0,"byteLength":4,"byteOffset":1564},{"buffer":0,"byteLength":120,"byteOffset":1568},{"buffer":0,"byteLength":504,"byteOffset":1688},{"buffer":0,"byteLength":504,"byteOffset":2192},{"buffer":0,"byteLength":4,"byteOffset":2696},{"buffer":0,"byteLength":168,"byteOffset":2700},{"buffer":0,"byteLength":432,"byteOffset":2868},{"buffer":0,"byteLength":432,"byteOffset":3300},{"buffer":0,"byteLength":4,"byteOffset":3732},{"buffer":0,"byteLength":108,"byteOffset":3736},{"buffer":0,"byteLength":16,"byteOffset":3844},{"buffer":0,"byteLength":3,"byteOffset":3860},{"buffer":0,"byteLength":4,"byteOffset":3863},{"buffer":0,"byteLength":3,"byteOffset":3867}],"buffers":[{"byteLength":3870,"bytes":"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\n5SmiPUdQTD+LjkS/ww/uPYICiT8/T5a/9AM6Pr/2zT/0+Ri/5SmiPUdQTD+T4ji/c08DPh46\njT+LjkS/ww/uPYICiT89Opm/K+AzPvHayz87JZy/YrwtPiO/yT+DOlC/n4DVPeXKhD8Y+xm/\n/gLpPfJ6Sj8Y+xm//gLpPfJ6Sj/Pupm/t0xXPkfwyj/Pupm/t0xXPkfwyj8Y+xm//gLpPfJ6\nSj+oO1G/3CwOPjvggz8Y+xm//gLpPfJ6Sj/Pupm/t0xXPkfwyj/Rz5a/gHBdPhUMzT+oO1G/\n3CwOPjvggz8Y+xm//gLpPfJ6Sj+f51y/SuUBPj1Rfz8Y+xm//gLpPfJ6Sj/Rz5a/gHBdPhUM\nzT+wj0W/bXQaPtcXiD8Y+xm//gLpPfJ6Sj+wj0W/bXQaPtcXiD+44zm//7smPnNPjD/Pupm/\nt0xXPkfwyj+oO1G/3CwOPjvggz/NpZy/7yhRPnnUyD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAD/EuI9Gid5v0ZQTj7/EuI9Gid5v0ZQTj7/EuI9Gid5v0ZQTj4wE+I9\nGCd5v1VQTj4wE+I9GCd5v1VQTj4wE+I9GCd5v1VQTj4hE+I9GCd5v1FQTj4hE+I9GCd5v1FQ\nTj4hE+I9GCd5v1FQTj7vEuI9Gid5vz5QTj7vEuI9Gid5vz5QTj7vEuI9Gid5vz5QTj4lE+I9\nGSd5v09QTj4lE+I9GSd5v09QTj4lE+I9GSd5v09QTj4wE+I9Fyd5v1NQTj4wE+I9Fyd5v1NQ\nTj4wE+I9Fyd5v1NQTj4AAAAAAAAAAAAAAIAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAIABE+K9\nGid5P0JQTr4BE+K9Gid5P0JQTr4BE+K9Gid5P0JQTr4yE+K9Fyd5P1NQTr4yE+K9Fyd5P1NQ\nTr4yE+K9Fyd5P1NQTr4pE+K9GCd5P1ZQTr4pE+K9GCd5P1ZQTr4pE+K9GCd5P1ZQTr4+E+K9\nFyd5P1RQTr4+E+K9Fyd5P1RQTr4+E+K9Fyd5P1RQTr40E+K9GCd5P1NQTr40E+K9GCd5P1NQ\nTr40E+K9GCd5P1NQTr4DE+K9GCd5P0FQTr4DE+K9GCd5P0FQTr4DE+K9GCd5P0FQTr4AAAAB\nyyJMv7YG5D1oDog/po1ev54j9T2pRJI/pFl9v7D9FD6cqKo/prNBvwA0vD2MWXI/q7dYv5i1\nAD53YJQ/siMyv9vy3D2DmH0/PtSJv0p0GT7+drM/8CNNv+dvFT6+I4c/y45fv1z+HT7/WZE/\nyFp+vz5qOD7yvak/y7RCv42GAT43hHA/zrhZvyQiJD7MdZM/1iQzv/rlET4uw3s/0FSKv9fg\nPD5UjLI/GPsZv/4C6T3yeko/uOM5v/+7Jj5zT4w/k+I4v3NPAz4eOo0/9PkYv+Upoj1HUEw/\nuOM5v/+7Jj5zT4w/sI9Fv210Gj7XF4g/i45Ev8MP7j2CAok/k+I4v3NPAz4eOo0/sI9Fv210\nGj7XF4g/0c+Wv4BwXT4VDM0/P0+Wv/QDOj6/9s0/i45Ev8MP7j2CAok/0c+Wv4BwXT4VDM0/\nz7qZv7dMVz5H8Mo/PTqZvyvgMz7x2ss/P0+Wv/QDOj6/9s0/z7qZv7dMVz5H8Mo/zaWcv+8o\nUT551Mg/OyWcv2K8LT4jv8k/PTqZvyvgMz7x2ss/zaWcv+8oUT551Mg/qDtRv9wsDj474IM/\ngzpQv5+A1T3lyoQ/OyWcv2K8LT4jv8k/qDtRv9wsDj474IM/n+dcv0rlAT49UX8/e+Zbv3zx\nvD1Jk4A/gzpQv5+A1T3lyoQ/n+dcv0rlAT49UX8/GPsZv/4C6T3yeko/9PkYv+Upoj1HUEw/\ne+Zbv3zxvD1Jk4A/GPsZv/4C6T3yeko/GPsZv/4C6T3yeko/9PkYv+Upoj1HUEw/9PkYv+Up\noj1HUEw/v71rP1GoND6pA7I+v71rP1GoND6pA7I+v71rP1GoND6pA7I+v71rP1GoND6pA7I+\ngXoZv/SWxT2fZUs/gXoZv/SWxT2fZUs/gXoZv/SWxT2fZUs/gXoZv/SWxT2fZUs/0O5KP8R/\nVT6rpRI/0O5KP8R/VT6rpRI/0O5KP8R/VT6rpRI/0O5KP8R/VT6rpRI/fXoZvxeWxT2jZUs/\nfXoZvxeWxT2jZUs/fXoZvxeWxT2jZUs/fXoZvxeWxT2jZUs/fXoZvxWWxT2lZUs/fXoZvxWW\nxT2lZUs/fXoZvxWWxT2lZUs/fXoZvxWWxT2lZUs/ze5KvxqAVb6npRK/ze5KvxqAVb6npRK/\nze5KvxqAVb6npRK/ze5KvxqAVb6npRK/k3oZv56WxT2SZUs/k3oZv56WxT2SZUs/k3oZv56W\nxT2SZUs/k3oZv56WxT2SZUs/fUscv4rLZ74SS0K/fUscv4rLZ74SS0K/fUscv4rLZ74SS0K/\nfUscv4rLZ74SS0K/AAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAA\nAAAAAAAAAAAAAdZuKb/x0Pc9l3dyPyI5P7/w4Q4++qiKPxcXeb8wPCo+TAerPwcFmL9WqEg+\ng3PMPwXwmr+NhEI+tVfKP4dhgr+g9B0+r8+mPxKRVr+apew9wjmCP8rwOr89+tI9wtBlP4Z6\nGb9xlsU9nWVLP5mYmD6ZmJg+mZiYPgAAgD8AAAABAQEBAAAA"}]},"context":{"shiny":false,"rmarkdown":null},"vertexShader":"#line 2 1\n// File 1 is the vertex shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\n\nattribute vec3 aPos;\nattribute vec4 aCol;\nuniform mat4 mvMatrix;\nuniform mat4 prMatrix;\nvarying vec4 vCol;\nvarying vec4 vPosition;\n\n#ifdef NEEDS_VNORMAL\nattribute vec3 aNorm;\nuniform mat4 normMatrix;\nvarying vec4 vNormal;\n#endif\n\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nattribute vec2 aTexcoord;\nvarying vec2 vTexcoord;\n#endif\n\n#ifdef FIXED_SIZE\nuniform vec3 textScale;\n#endif\n\n#ifdef FIXED_QUADS\nattribute vec3 aOfs;\n#endif\n\n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\nvarying float normz;\nuniform mat4 invPrMatrix;\n#else\nattribute vec3 aPos1;\nattribute vec3 aPos2;\nvarying float normz;\n#endif\n#endif // IS_TWOSIDED\n\n#ifdef FAT_LINES\nattribute vec3 aNext;\nattribute vec2 aPoint;\nvarying vec2 vPoint;\nvarying float vLength;\nuniform float uAspect;\nuniform float uLwd;\n#endif\n\n#ifdef USE_ENVMAP\nvarying vec3 vReflection;\n#endif\n\nvoid main(void) {\n  \n#ifndef IS_BRUSH\n#if defined(NCLIPPLANES) || !defined(FIXED_QUADS) || defined(HAS_FOG) || defined(USE_ENVMAP)\n  vPosition = mvMatrix * vec4(aPos, 1.);\n#endif\n  \n#ifndef FIXED_QUADS\n  gl_Position = prMatrix * vPosition;\n#endif\n#endif // !IS_BRUSH\n  \n#ifdef IS_POINTS\n  gl_PointSize = POINTSIZE;\n#endif\n  \n  vCol = aCol;\n  \n// USE_ENVMAP implies NEEDS_VNORMAL\n\n#ifdef NEEDS_VNORMAL\n  vNormal = normMatrix * vec4(-aNorm, dot(aNorm, aPos));\n#endif\n\n#ifdef USE_ENVMAP\n  vReflection = normalize(reflect(vPosition.xyz/vPosition.w, \n                        normalize(vNormal.xyz/vNormal.w)));\n#endif\n  \n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\n  /* normz should be calculated *after* projection */\n  normz = (invPrMatrix*vNormal).z;\n#else\n  vec4 pos1 = prMatrix*(mvMatrix*vec4(aPos1, 1.));\n  pos1 = pos1/pos1.w - gl_Position/gl_Position.w;\n  vec4 pos2 = prMatrix*(mvMatrix*vec4(aPos2, 1.));\n  pos2 = pos2/pos2.w - gl_Position/gl_Position.w;\n  normz = pos1.x*pos2.y - pos1.y*pos2.x;\n#endif\n#endif // IS_TWOSIDED\n  \n#ifdef NEEDS_VNORMAL\n  vNormal = vec4(normalize(vNormal.xyz/vNormal.w), 1);\n#endif\n  \n#if defined(HAS_TEXTURE) || defined(IS_TEXT)\n  vTexcoord = aTexcoord;\n#endif\n  \n#if defined(FIXED_SIZE) && !defined(ROTATING)\n  vec4 pos = prMatrix * mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w;\n  gl_Position = pos + vec4(aOfs*textScale, 0.);\n#endif\n  \n#if defined(IS_SPRITES) && !defined(FIXED_SIZE)\n  vec4 pos = mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w + vec4(aOfs,  0.);\n  gl_Position = prMatrix*pos;\n#endif\n  \n#ifdef FAT_LINES\n  /* This code was inspired by Matt Deslauriers' code in \n   https://mattdesl.svbtle.com/drawing-lines-is-hard */\n  vec2 aspectVec = vec2(uAspect, 1.0);\n  mat4 projViewModel = prMatrix * mvMatrix;\n  vec4 currentProjected = projViewModel * vec4(aPos, 1.0);\n  currentProjected = currentProjected/currentProjected.w;\n  vec4 nextProjected = projViewModel * vec4(aNext, 1.0);\n  vec2 currentScreen = currentProjected.xy * aspectVec;\n  vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\n  float len = uLwd;\n  vec2 dir = vec2(1.0, 0.0);\n  vPoint = aPoint;\n  vLength = length(nextScreen - currentScreen)/2.0;\n  vLength = vLength/(vLength + len);\n  if (vLength > 0.0) {\n    dir = normalize(nextScreen - currentScreen);\n  }\n  vec2 normal = vec2(-dir.y, dir.x);\n  dir.x /= uAspect;\n  normal.x /= uAspect;\n  vec4 offset = vec4(len*(normal*aPoint.x*aPoint.y - dir), 0.0, 0.0);\n  gl_Position = currentProjected + offset;\n#endif\n  \n#ifdef IS_BRUSH\n  gl_Position = vec4(aPos, 1.);\n#endif\n}","fragmentShader":"#line 2 2\n// File 2 is the fragment shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\nvarying vec4 vCol; // carries alpha\nvarying vec4 vPosition;\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nvarying vec2 vTexcoord;\nuniform sampler2D uSampler;\n#endif\n\n#ifdef HAS_FOG\nuniform int uFogMode;\nuniform vec3 uFogColor;\nuniform vec4 uFogParms;\n#endif\n\n#if defined(IS_LIT) && !defined(FIXED_QUADS)\nvarying vec4 vNormal;\n#endif\n\n#if NCLIPPLANES > 0\nuniform vec4 vClipplane[NCLIPPLANES];\n#endif\n\n#if NLIGHTS > 0\nuniform mat4 mvMatrix;\n#endif\n\n#ifdef IS_LIT\nuniform vec3 emission;\nuniform float shininess;\n#if NLIGHTS > 0\nuniform vec3 ambient[NLIGHTS];\nuniform vec3 specular[NLIGHTS]; // light*material\nuniform vec3 diffuse[NLIGHTS];\nuniform vec3 lightDir[NLIGHTS];\nuniform bool viewpoint[NLIGHTS];\nuniform bool finite[NLIGHTS];\n#endif\n#endif // IS_LIT\n\n#ifdef IS_TWOSIDED\nuniform bool front;\nvarying float normz;\n#endif\n\n#ifdef FAT_LINES\nvarying vec2 vPoint;\nvarying float vLength;\n#endif\n\n#ifdef USE_ENVMAP\nvarying vec3 vReflection;\n#endif\n\nvoid main(void) {\n  vec4 fragColor;\n#ifdef FAT_LINES\n  vec2 point = vPoint;\n  bool neg = point.y < 0.0;\n  point.y = neg ? (point.y + vLength)/(1.0 - vLength) :\n                 -(point.y - vLength)/(1.0 - vLength);\n#if defined(IS_TRANSPARENT) && defined(IS_LINESTRIP)\n  if (neg && length(point) <= 1.0) discard;\n#endif\n  point.y = min(point.y, 0.0);\n  if (length(point) > 1.0) discard;\n#endif // FAT_LINES\n  \n#ifdef ROUND_POINTS\n  vec2 coord = gl_PointCoord - vec2(0.5);\n  if (length(coord) > 0.5) discard;\n#endif\n  \n#if NCLIPPLANES > 0\n  for (int i = 0; i < NCLIPPLANES; i++)\n    if (dot(vPosition, vClipplane[i]) < 0.0) discard;\n#endif\n    \n#ifdef FIXED_QUADS\n    vec3 n = vec3(0., 0., 1.);\n#elif defined(IS_LIT)\n    vec3 n = normalize(vNormal.xyz);\n#endif\n    \n#ifdef IS_TWOSIDED\n    if ((normz <= 0.) != front) discard;\n#endif\n\n#ifdef IS_LIT\n    vec3 eye = normalize(-vPosition.xyz/vPosition.w);\n    vec3 lightdir;\n    vec4 colDiff;\n    vec3 halfVec;\n    vec4 lighteffect = vec4(emission, 0.);\n    vec3 col;\n    float nDotL;\n#ifdef FIXED_QUADS\n    n = -faceforward(n, n, eye);\n#endif\n    \n#if NLIGHTS > 0\n    for (int i=0;i<NLIGHTS;i++) {\n      colDiff = vec4(vCol.rgb * diffuse[i], vCol.a);\n      lightdir = lightDir[i];\n      if (!viewpoint[i])\n        lightdir = (mvMatrix * vec4(lightdir, 1.)).xyz;\n      if (!finite[i]) {\n        halfVec = normalize(lightdir + eye);\n      } else {\n        lightdir = normalize(lightdir - vPosition.xyz/vPosition.w);\n        halfVec = normalize(lightdir + eye);\n      }\n      col = ambient[i];\n      nDotL = dot(n, lightdir);\n      col = col + max(nDotL, 0.) * colDiff.rgb;\n      col = col + pow(max(dot(halfVec, n), 0.), shininess) * specular[i];\n      lighteffect = lighteffect + vec4(col, colDiff.a);\n    }\n#endif\n    \n#else // not IS_LIT\n    vec4 colDiff = vCol;\n    vec4 lighteffect = colDiff;\n#endif\n    \n#ifdef IS_TEXT\n    vec4 textureColor = lighteffect*texture2D(uSampler, vTexcoord);\n#endif\n    \n#ifdef HAS_TEXTURE\n\n// These calculations use the definitions from \n// https://docs.gl/gl3/glTexEnv\n\n#ifdef USE_ENVMAP\n    float m = 2.0 * sqrt(dot(vReflection, vReflection) + 2.0*vReflection.z + 1.0);\n    vec4 textureColor = texture2D(uSampler, vReflection.xy / m + vec2(0.5, 0.5));\n#else\n    vec4 textureColor = texture2D(uSampler, vTexcoord);\n#endif\n\n#ifdef TEXTURE_rgb\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(textureColor.rgb, lighteffect.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = lighteffect*vec4(textureColor.rgb, 1.);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - textureColor.rgb) * lighteffect.rgb, lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(lighteffect.rgb + textureColor.rgb, lighteffect.a);\n#endif\n\n#endif //TEXTURE_rgb\n        \n#ifdef TEXTURE_rgba\n\n#ifdef TEXMODE_replace\n// already done\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = lighteffect*textureColor;\n#endif\n\n#ifdef TEXMODE_decal\n    textureColor = vec4((1. - textureColor.a)*lighteffect.rgb) +\n                     textureColor.a*textureColor.rgb, \n                     lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - textureColor.rgb) * lighteffect.rgb,\n                    lighteffect.a*textureColor.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(lighteffect.rgb + textureColor.rgb,\n                    lighteffect.a*textureColor.a);\n#endif\n    \n#endif //TEXTURE_rgba\n    \n#ifdef TEXTURE_alpha\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(lighteffect.rgb, textureColor.a);\n#endif \n\n#if defined(TEXMODE_modulate) || defined(TEXMODE_blend) || defined(TEXMODE_add)\n    textureColor = vec4(lighteffect.rgb, lighteffect.a*textureColor.a);\n#endif\n \n#endif\n    \n// The TEXTURE_luminance values are not from that reference    \n#ifdef TEXTURE_luminance\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(luminance, luminance, luminance, lighteffect.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = vec4(luminance*lighteffect.rgb, lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - luminance)*lighteffect.rgb,\n                        lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(luminance + lighteffect.rgb, lighteffect.a);\n#endif\n\n#endif // TEXTURE_luminance\n \n    \n#ifdef TEXTURE_luminance_alpha\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n\n#if defined(TEXMODE_replace) || defined(TEXMODE_decal)\n    textureColor = vec4(luminance, luminance, luminance, textureColor.a);\n#endif \n\n#ifdef TEXMODE_modulate\n    textureColor = vec4(luminance*lighteffect.rgb, \n                        textureColor.a*lighteffect.a);\n#endif\n\n#ifdef TEXMODE_blend\n    textureColor = vec4((1. - luminance)*lighteffect.rgb,\n                        textureColor.a*lighteffect.a);\n#endif\n\n#ifdef TEXMODE_add\n    textureColor = vec4(luminance + lighteffect.rgb, \n                        textureColor.a*lighteffect.a);\n\n#endif\n\n#endif // TEXTURE_luminance_alpha\n    \n    fragColor = textureColor;\n\n#elif defined(IS_TEXT)\n    if (textureColor.a < 0.1)\n      discard;\n    else\n      fragColor = textureColor;\n#else\n    fragColor = lighteffect;\n#endif // HAS_TEXTURE\n    \n#ifdef HAS_FOG\n    // uFogParms elements: x = near, y = far, z = fogscale, w = (1-sin(FOV/2))/(1+sin(FOV/2))\n    // In Exp and Exp2: use density = density/far\n    // fogF will be the proportion of fog\n    // Initialize it to the linear value\n    float fogF;\n    if (uFogMode > 0) {\n      fogF = (uFogParms.y - vPosition.z/vPosition.w)/(uFogParms.y - uFogParms.x);\n      if (uFogMode > 1)\n        fogF = mix(uFogParms.w, 1.0, fogF);\n      fogF = fogF*uFogParms.z;\n      if (uFogMode == 2)\n        fogF = 1.0 - exp(-fogF);\n      // Docs are wrong: use (density*c)^2, not density*c^2\n      // https://gitlab.freedesktop.org/mesa/mesa/-/blob/master/src/mesa/swrast/s_fog.c#L58\n      else if (uFogMode == 3)\n        fogF = 1.0 - exp(-fogF*fogF);\n      fogF = clamp(fogF, 0.0, 1.0);\n      gl_FragColor = vec4(mix(fragColor.rgb, uFogColor, fogF), fragColor.a);\n    } else gl_FragColor = fragColor;\n#else\n    gl_FragColor = fragColor;\n#endif // HAS_FOG\n    \n}","players":[],"webGLoptions":{"preserveDrawingBuffer":true}},"evals":[],"jsHooks":[]}