水晶体效果
本文示例用随机几何体, 光照, 纹理贴图, 着色器整合起来绘制一个水晶簇. 通过调整着色器, 可以产生岩浆, 蓝宝石等各种效果.
示例效果图:
27 个单晶 45 度随机分布的蓝色水晶簇.
uv
77 个单晶 180 度随机分布的红色水晶簇.
uv
制作随机水晶体生成器
很多三维库的几何体基本都是规则几何体, 没有随机的美感, 因此捣鼓了一个随机水晶生成器, 源码地址: https://github.com/guoweish/vertex.gl
实现思路如下:
生成单个水晶顶点
uv
生成水晶簇顶点
uv
实现步骤如下:
一个圆周上取多边形点, 使用随机角度, 形成不同宽度面
- ...
- angle = i / faceNumber * PI * 2 + r * angleDithringFactor * angleDithringUnit;
- px = Math.sin(angle) * polygonCircleRadius;
- py = Math.cos(angle) * polygonCircleRadius;
- ...
随机缩放圆半径, 圆周上同角度取多边形点, 形成不同柱体半径
- ...
- pxd = Math.sin(angle) * polygonCircleRadius* (circleRadiusDithringFactor + radiusDithringDistance);
- pyd = Math.cos(angle) * polygonCircleRadius* (circleRadiusDithringFactor + radiusDithringDistance);
- ...
形成多边形棱柱的上下两层顶点
- ...
- vertexs.topPositions.push(px);
- vertexs.topPositions.push(py);
- ...
增加正对圆心的顶点, 形成水晶柱顶点
- ...
- vertexsCon = [0, cylinderHeight+conHeight, 0];
- ...
生成一个水晶体几何体
- ...
- vertexs = [...vertexsCylinderDown, ...vertexsCylinderTop, ...vertexsCon];
- ...
矩阵变换缩放, 旋转单个水晶体的顶点, 并复制顶点
- ...
- let scaleMatrix = new Matrix4();
- scaleMatrix.setScale(scaleFactor, scaleFactor, scaleFactor);
- let rotateMatrix = new Matrix4();
- rotateMatrix.setRotate(rotateAngle, rotateCenter.x, rotateCenter.y, rotateCenter.z);
- ...
- let vScaledRotated = rotateMatrix.multiplyVector4(vScaled);
- ...
生成水晶簇几何体
- ...
- let extendedIndices = extendIndices(cristalTransformed.indices, currentIndicesLength);
- cluster.indices = cluster.indices.concat(extendedIndices);
- cluster.positions = cluster.positions.concat(cristalTransformed.positions);
- cluster.uvs = cluster.uvs.concat(cristal.uvs);
- ...
渲染几何体
用纯色 shader 简单渲染一下是否顶点和索引正确;
片元着色器
fragColor = vec4(0.6, 0.6, 0.6, 1.0);
uv
添加光照
增加光照, 使用 Blinn-Phong 模型;
片元着色器
- vec3 diffuse = max(dot(normal, ec_lightDirection), 0.0) * lightColor * lightIntensity * baseColor;
- vec3 viewDirection = -normalize(ec_position);
- vec3 halfAngle = normalize(viewDirection + ec_lightDirection);
- float specularFactor = clamp(dot(normal, halfAngle), 0.0, 1.0);
- float spec = pow(specularFactor, specularIntensity);
- vec3 specular = clamp(spec * specularColor, 0.0, 1.0);
- fragColor = vec4(diffuse + specular + ambient, 1.0);
着色器添加渐变的祖母绿效果
为了看上去像祖母绿宝石效果, 用纹理做一个渐变; 用 pow 函数使得渐变非线性, 用 mix 函数融合得颜色, 看上去效果更自然;
片元着色器
- ...
- float colorMixFactor = pow(v_uv.y, 3.0);
- ...
- vec3 baseColor = mix(CRISTAL_COLOR, GEM_COLOR_GREEN, colorMixFactor);
- ...
uv
使用贴图增强表面细节
为了表面有石头纹样效果, 找一张大理石图片做贴图过滤一下颜色; 魔改一下光照模型, 贴图过滤 diffuse 而不过滤 specular, 这样使得表面反色不受影响而产生表面光滑的效果;
片元着色器
- ...
- fragColor = vec4(diffuse + diffuse * textureFilter + specular + AMB_COLOR, 1.0);
uv
结语
可以使用 perlin 噪音和 fbm 让随机几何体的视觉效果自然, 比如大小个体比例和空间位置的分布; 光照可以改用 pbr 模型, 比 Blinn-Phong 会更好, 计划下一篇更新尝试 (希望有空填坑 -_-!!).
- webgl Programing Guide
- OpenGl Shading Language
- The Book of Shaders https://thebookofshaders.com
- ShaderToy https://www.shadertoy.com
来源: https://juejin.im/post/5c7290b36fb9a049b07dfb98