如何使用WebGL渲染一簇水晶

水晶體效果

本文示例用隨機幾何體、光照、紋理貼圖、着色器整合起來繪製一個水晶簇。經過調整着色器,能夠產生岩漿、藍寶石等各類效果。git

示例效果圖:github

27個單晶45度隨機分佈的藍色水晶簇。

canvas-preview
uv

77個單晶180度隨機分佈的紅色水晶簇。

canvas-preview
uv

製做隨機水晶體生成器

不少三維庫的幾何體基本都是規則幾何體,沒有隨機的美感,所以搗鼓了一個隨機水晶生成器,源碼地址:github.com/guoweish/ve…web

實現思路以下:canvas

  • 生成單個水晶頂點

canvas-preview
uv

  • 生成水晶簇頂點

canvas-preview
uv

實現步驟以下:bash

  • 一個圓周上取多邊形點,使用隨機角度,造成不一樣寬度面
...
  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簡單渲染一下是否頂點和索引正確;ide

  • 片元着色器
fragColor = vec4(0.6, 0.6, 0.6, 1.0);
複製代碼

canvas-preview
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);
複製代碼

canvas-preview

着色器添加漸變的祖母綠效果

爲了看上去像祖母綠寶石效果,用紋理作一個漸變;用pow函數使得漸變非線性,用mix函數融合得顏色,看上去效果更天然;webgl

  • 片元着色器
...
  float colorMixFactor = pow(v_uv.y, 3.0);
  ...
  vec3 baseColor = mix(CRISTAL_COLOR, GEM_COLOR_GREEN,  colorMixFactor);
  ...
複製代碼

canvas-preview
uv

使用貼圖加強表面細節

爲了表面有石頭紋樣效果,找一張大理石圖片作貼圖過濾一下顏色;魔改一下光照模型,貼圖過濾diffuse而不過濾specular,這樣使得表面反色不受影響而產生表面光滑的效果;ui

  • 片元着色器
...
  fragColor = vec4(diffuse + diffuse * textureFilter + specular + AMB_COLOR, 1.0);
複製代碼

canvas-preview
uv

結語

能夠使用perlin噪音和fbm讓隨機幾何體的視覺效果天然,好比大小個體比例和空間位置的分佈;光照能夠改用pbr模型,比Blinn-Phong會更好,計劃下一篇更新嘗試(但願有空填坑-_-!!)。spa

關於做者

郭不耐 github.com/guoweish

參考文獻

Webgl Programing Guide

OpenGl Shading Language

The Book of Shaders

ShaderToy

相關文章
相關標籤/搜索