我想让小球的12个面随着音乐节奏跳动,下面代码实现了12个面的随机跳跃,怎么把音频节奏加上
const VSHADER_SOURCE = `
precision highp float;
attribute vec2 aPosition;
varying vec3 vFacePlane;
varying vec3 vNc;
varying vec3 vPlane;
#define i3 0.5773502691896258
#define sqrt3 1.7320508075688772
// 顶点着色器新增
varying mat2 vCart2Hex;
varying mat2 vHex2Cart;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
float cospin = cos(3.14159265359 / 5.0),
scospin=sqrt(0.75-cospin*cospin);
vNc=vec3(-0.5,-cospin,scospin);// 镜像平面法线
vFacePlane=normalize(vec3(0.0,scospin,cospin));// 预计算二十面体坐标系
vPlane = cross(vec3(1,0,0), vFacePlane);// 横向方向
// 矩阵定义
vCart2Hex = mat2(1, 0, i3, 2. * i3);
vHex2Cart = mat2(1, 0, -.5, .5 * sqrt3);
}`,FSHADER_SOURCE=`
precision highp float;
uniform vec2 uResolution;
uniform float uTime;
uniform vec2 uMouse;
uniform int uMouseControl;
uniform int uLoop;
// 添加音频uniform变量
uniform sampler2D uAudioTexture; // 音频频谱纹理
uniform float uAudioPeak; // 音频峰值(0-1)
uniform float uAudioEnergy; // 音频能量(0-1)
// 常量定义
#define PI 3.14159265359
#define TAU 6.283185307179586
#define PHI 1.618033988749895
// 从顶点着色器接收预计算的常量
varying vec3 vFacePlane;
varying vec3 vNc;
varying vec3 vPlane;
// 顶点着色器新增
varying mat2 vCart2Hex;
varying mat2 vHex2Cart;
// 颜色常量
const vec3 FACE_COLOR = vec3(.9,.9,1.);
const vec3 BACK_COLOR = vec3(.1,.1,.15);
const vec3 BACKGROUND_COLOR = vec3(.0, .005, .03);
// 添加音频相关函数
float getAudioValue(vec3 hexCenter) {
// 使用六边形中心位置作为UV坐标的种子
float hash = dot(hexCenter, vec3(12.9898,78.233,45.164));
hash = fract(sin(hash) * 43758.5453);
// 从音频纹理中采样(这里简化处理,实际实现可能需要更复杂的采样)
// 假设音频纹理是1x256的频谱数据
float freqIndex = hash * 255.0;
vec2 texCoord = vec2(freqIndex / 256.0, 0.5);
// 简单模拟:实际使用时应该从uAudioTexture采样
// 这里用峰值和能量模拟音频响应
float audioValue = uAudioPeak * 0.5 + uAudioEnergy * 0.3;
// 添加一些基于位置的随机变化
audioValue *= mix(0.8, 1.2, hash);
return audioValue;
}
// HG_SDF 函数
void pR(inout vec2 p, float a) {
p = cos(a)*p + sin(a)*vec2(p.y, -p.x);
}
float pReflect(inout vec3 p, vec3 planeNormal, float offset) {
float t = dot(p, planeNormal)+offset;
if (t < 0.) {
p = p - (2.*t)*planeNormal;
}
return sign(t);
}
float smax(float a, float b, float r) {
float m = max(a, b);
if ((-a < r) && (-b < r)) {
return max(m, -(r - sqrt((r+a)*(r+a) + (r+b)*(r+b))));
} else {
return m;
}
}
// 二十面体域镜像
void pModIcosahedron(inout vec3 p) {
p = abs(p);
pReflect(p, vNc, 0.);
p.xy = abs(p.xy);
pReflect(p, vNc, 0.);
p.xy = abs(p.xy);
pReflect(p, vNc, 0.);
}
// 三角形平铺
struct TriPoints {vec2 a;vec2 b;vec2 c;vec2 center;vec2 ab;vec2 bc;vec2 ca;};
TriPoints closestTriPoints(vec2 p) {
vec2 pTri = vCart2Hex * p;
vec2 pi = floor(pTri);
vec2 pf = fract(pTri);
float split1 = step(pf.y, pf.x);
float split2 = step(pf.x, pf.y);
vec2 a = vec2(split1, 1);
vec2 b = vec2(1, split2);
vec2 c = vec2(0, 0);
a += pi;
b += pi;
c += pi;
a = vHex2Cart * a;
b = vHex2Cart * b;
c = vHex2Cart * c;
vec2 center = (a + b + c) / 3.;
vec2 ab = (a + b) / 2.;
vec2 bc = (b + c) / 2.;
vec2 ca = (c + a) / 2.;
return TriPoints(a, b, c, center, ab, bc, ca);
}
// 测地线平铺
struct TriPoints3D {vec3 a;vec3 b;vec3 c;vec3 center;vec3 ab;vec3 bc;vec3 ca;};
// 二十面体面坐标计算(优化后)
vec2 icosahedronFaceCoordinates(vec3 p) {
vec3 pn = normalize(p);
// 使用向量投影替代交点计算
float t = dot(vFacePlane, vFacePlane) / max(dot(pn, vFacePlane), 0.001);
vec3 i = pn * t;
return vec2(dot(i, vPlane), dot(i,vec3(1,0,0)));
}
// 球面映射函数(优化后)
vec3 faceToSphere(vec2 facePoint) {// 合并向量缩放和归一化
return normalize(vFacePlane + (vPlane * facePoint.x) + (vec3(1,0,0) * facePoint.y));
}
TriPoints3D geodesicTriPoints(vec3 p, float subdivisions) {
vec2 uv = icosahedronFaceCoordinates(p); // 原始 UV 坐标
float uvScale = subdivisions / 0.3819660112501051 / 2.0;
// 直接使用未缩放的 uv 计算网格顶点
TriPoints points = closestTriPoints(uv*uvScale);
// 映射到球面(使用预计算的坐标系)
vec3 a = faceToSphere(points.a/uvScale);
vec3 b = faceToSphere(points.b/uvScale);
vec3 c = faceToSphere(points.c/uvScale);
vec3 center = faceToSphere(points.center/uvScale);
vec3 ab = faceToSphere(points.ab/uvScale);
vec3 bc = faceToSphere(points.bc/uvScale);
vec3 ca = faceToSphere(points.ca/uvScale);
return TriPoints3D(a, b, c, center, ab, bc, ca);
}
// 光谱颜色调色板
vec3 pal(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
return a + b*cos(6.28318*(c*t+d));
}
vec3 spectrum(float n) {
return pal(n, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.0,0.33,0.67));
}
// 模型/相机旋转
mat3 sphericalMatrix(float theta, float phi) {
float cx = cos(theta);
float cy = cos(phi);
float sx = sin(theta);
float sy = sin(phi);
return mat3(
cy, -sy * -sx, -sy * cx,
0, cx, sx,
sy, cy * -sx, cy * cx
);
}
mat3 mouseRotation(bool enable, vec2 xy) {
if (enable) {
vec2 mouse = uMouse / uResolution;
if (mouse.x != 0. && mouse.y != 0.) {
xy.x = mouse.x;
xy.y = mouse.y;
}
}
float rx, ry;
rx = (xy.y + .5) * PI;
ry = (-xy.x) * 2. * PI;
return sphericalMatrix(rx, ry);
}
mat3 modelRotation() {
vec2 modelRotation = vec2(.3, .25);
mat3 m = mouseRotation(uMouseControl == 1, modelRotation);
return m;
}
mat3 cameraRotation() {
vec2 cameraRotation = vec2(.5, .5);
mat3 m = mouseRotation(uMouseControl == 2, cameraRotation);
return m;
}
const float SCENE_DURATION = 6.;
const float CROSSFADE_DURATION = 2.;
float time;
struct HexSpec {
float roundTop;
float roundCorner;
float height;
float thickness;
float gap;
};
HexSpec newHexSpec(float subdivisions) {
return HexSpec(
.05 / subdivisions, // roundTop - 减小这个值会使六边形顶部更尖锐
.1 / subdivisions, // roundCorner - 减小这个值会使角更锐利
2.0, // height - 直接影响六边形的高度/大小
2.0, // thickness - 墙的厚度
0.005 // gap - 六边形间的间隙
);
}
// 修改后的动画函数,添加音频响应
HexSpec animHex1(vec3 hexCenter, float subdivisions) {
HexSpec spec = newHexSpec(subdivisions);
float offset = time * 3.0 * PI;
offset -= subdivisions;
// 获取音频值
float audioValue = getAudioValue(hexCenter);
float audioEffect = audioValue * 0.5; // 限制音频影响范围
float blend = dot(hexCenter, vFacePlane);
blend = cos(blend * 30.0 + offset) * 0.5 + 0.5;
spec.height = mix(1.75, 2.0, blend) + audioEffect * 1.0; // 音频使高度增加
spec.thickness = spec.height + audioEffect * 0.5;// 厚度也响应音频
return spec;
}
HexSpec animHex2(vec3 hexCenter, float subdivisions) {
HexSpec spec = newHexSpec(subdivisions);
// 获取音频值
float audioValue = getAudioValue(hexCenter);
float audioEffect = audioValue * 0.8; // 更大的音频影响
// 基础动画 + 音频响应
float blend = hexCenter.y;
spec.height = mix(1.6, 2., sin(blend * 10. + time * PI) * .5 + .5) + audioEffect * 1.2;
spec.roundTop = .02 / subdivisions;
spec.roundCorner = .09 / subdivisions;
spec.thickness = spec.roundTop * 4. + audioEffect * 0.3;
spec.gap = .01 + audioEffect * 0.02; // 音频使间隙变大
return spec;
}
HexSpec animHex3(vec3 hexCenter, float subdivisions) {
HexSpec spec = newHexSpec(subdivisions);
// 获取音频值
float audioValue = getAudioValue(hexCenter);
float audioEffect = audioValue * 0.7;
// 基础动画 + 音频响应
float blend = acos(dot(hexCenter,vec3(0,0,1))) * 10.;
blend = cos(blend + time * PI) * .5 + .5;
// 间隙变化改为由音频驱动
spec.gap = mix(.01, .4, blend) / subdivisions + audioEffect * 0.1;
spec.thickness = spec.roundTop * 2. + audioEffect * 0.2;
// 高度也响应音频
spec.height = 2.0 + audioEffect * 1.5;
return spec;
}
// 动画过渡
float sineInOut(float t) {
return -0.5 * (cos(PI * t) - 1.0);
}
float transitionValues(float a, float b, float c) {
if(uLoop==1){
return a;
}else if(uLoop==2){
return b;
}else if(uLoop==3){
return c;
}
float t = time / SCENE_DURATION;
float scene = floor(mod(t, 3.));
float blend = fract(t);
float delay = (SCENE_DURATION - CROSSFADE_DURATION) / SCENE_DURATION;
blend = max(blend - delay, 0.) / (1. - delay);
blend = sineInOut(blend);
float ab = mix(a, b, blend);
float bc = mix(b, c, blend);
float cd = mix(c, a, blend);
float result = mix(ab, bc, min(scene, 1.));
result = mix(result, cd, max(scene - 1., 0.));
return result;
}
HexSpec transitionHexSpecs(HexSpec a, HexSpec b, HexSpec c) {
float roundTop = transitionValues(a.roundTop, b.roundTop, c.roundTop);
float roundCorner = transitionValues(a.roundCorner, b.roundCorner, c.roundCorner);
float height = transitionValues(a.height, b.height, c.height);
float thickness = transitionValues(a.thickness, b.thickness, c.thickness);
float gap = transitionValues(a.gap, b.gap, c.gap);
return HexSpec(roundTop, roundCorner, height, thickness, gap);
}
struct Model {float dist;vec3 albedo;float glow;};
Model hexModel(vec3 p,vec3 hexCenter,vec3 edgeA,vec3 edgeB,HexSpec spec){
float d;
float edgeADist = dot(p, edgeA) + spec.gap;
float edgeBDist = dot(p, edgeB) - spec.gap;
float edgeDist = smax(edgeADist, -edgeBDist, spec.roundCorner);
float outerDist = length(p) - spec.height;
d = smax(edgeDist, outerDist, spec.roundTop);
float innerDist = length(p) - spec.height + spec.thickness;
d = smax(d, -innerDist, spec.roundTop);
vec3 color;
float faceBlend = (spec.height - length(p)) / spec.thickness;
faceBlend = clamp(faceBlend, 0., 1.);
color = mix(FACE_COLOR, BACK_COLOR, step(.5, faceBlend));
// 边缘颜色也响应音频
float audioValue = getAudioValue(hexCenter);
vec3 edgeColor = spectrum(dot(hexCenter, vFacePlane) * 5. + length(p) + .8 + audioValue * 2.0);
float edgeBlend = smoothstep(-.04, -.005, edgeDist);
color = mix(color, edgeColor, edgeBlend);
return Model(d, color, edgeBlend);
}
Model opU(Model m1, Model m2) {
if (m1.dist < m2.dist) {
return m1;
} else {
return m2;
}
}
Model geodesicModel(vec3 p) {
pModIcosahedron(p);
float subdivisions = transitionValues(
mix(2.4, 3.4, cos(time * PI) * 0.5 + 0.5), // 动画1的细分级别
mix(1.0, 2.3, sin(time * PI/2.) * .5 + .5), // 动画2的细分级别
5.0 // 动画3的细分级别(增大这个值会生成更多但更小的六边形)
);
TriPoints3D points = geodesicTriPoints(p, subdivisions);
vec3 edgeAB = normalize(cross(points.center, points.ab));
vec3 edgeBC = normalize(cross(points.center, points.bc));
vec3 edgeCA = normalize(cross(points.center, points.ca));
Model model, part;
HexSpec spec;
spec = transitionHexSpecs(
animHex1(points.b, subdivisions),
animHex2(points.b, subdivisions),
animHex3(points.b, subdivisions)
);
part = hexModel(p, points.b, edgeAB, edgeBC, spec);
model = part;
spec = transitionHexSpecs(
animHex1(points.c, subdivisions),
animHex2(points.c, subdivisions),
animHex3(points.c, subdivisions)
);
part = hexModel(p, points.c, edgeBC, edgeCA, spec);
model = opU(model, part);
spec = transitionHexSpecs(
animHex1(points.a, subdivisions),
animHex2(points.a, subdivisions),
animHex3(points.a, subdivisions)
);
part = hexModel(p, points.a, edgeCA, edgeAB, spec);
model = opU(model, part);
return model;
}
Model map(vec3 p) {
mat3 m = modelRotation();
p *= m;
if (uLoop==0){
pR(p.xz, time * PI/16.);
}
Model model = geodesicModel(p);
return model;
}
vec3 doLighting(Model model, vec3 pos, vec3 nor, vec3 ref, vec3 rd) {
vec3 lightPos = normalize(vec3(.5,.5,-1.));
vec3 backLightPos = normalize(vec3(-.5,-.3,1));
vec3 ambientPos = vec3(0,1,0);
vec3 lig = lightPos;
float amb = clamp((dot(nor, ambientPos) + 1.) / 2., 0., 1.);
float dif = clamp(dot(nor, lig), 0.0, 1.0);
float bac = pow(clamp(dot(nor, backLightPos), 0., 1.), 1.5);
float fre = pow(clamp(1.0+dot(nor,rd),0.0,1.0), 2.0);
vec3 lin = vec3(0.0);
lin += 1.20 * dif * vec3(.9);
lin += 0.80 * amb * vec3(.5, .7, .8);
lin += 0.30 * bac * vec3(.25);
lin += 0.20 * fre * vec3(1);
vec3 albedo = model.albedo;
vec3 col = mix(albedo * lin, albedo, model.glow);
return col;
}
const float MAX_TRACE_DISTANCE = 8.;
const float INTERSECTION_PRECISION = .001;
const int NUM_OF_TRACE_STEPS = 100;
const float FUDGE_FACTOR = .9;
struct CastRay {vec3 origin;vec3 direction;};
struct Ray {
vec3 origin;
vec3 direction;
float len;
};
struct Hit {
Ray ray;
Model model;
vec3 pos;
bool isBackground;
vec3 normal;
vec3 color;
};
vec3 calcNormal(in vec3 pos) {
vec3 eps = vec3(0.001, 0.0, 0.0);
vec3 nor = vec3(
map(pos+eps.xyy).dist - map(pos-eps.xyy).dist,
map(pos+eps.yxy).dist - map(pos-eps.yxy).dist,
map(pos+eps.yyx).dist - map(pos-eps.yyx).dist);
return normalize(nor);
}
Hit raymarch(CastRay castRay) {
float currentDist = INTERSECTION_PRECISION * 2.0;
Model model;
Ray ray = Ray(castRay.origin, castRay.direction, 0.);
for(int i=0; i<NUM_OF_TRACE_STEPS; i++) {
if (currentDist < INTERSECTION_PRECISION || ray.len > MAX_TRACE_DISTANCE) {
break;
}
model = map(ray.origin + ray.direction * ray.len);
currentDist = model.dist;
ray.len += currentDist * FUDGE_FACTOR;
}
bool isBackground = false;
vec3 pos = vec3(0);
vec3 normal = vec3(0);
vec3 color = vec3(0);
if (ray.len > MAX_TRACE_DISTANCE) {
isBackground = true;
} else {
pos = ray.origin + ray.direction * ray.len;
normal = calcNormal(pos);
}
return Hit(ray, model, pos, isBackground, normal, color);
}
void shadeSurface(inout Hit hit) {
vec3 color = BACKGROUND_COLOR;
if (hit.isBackground) {
hit.color = color;
return;
}
vec3 ref = reflect(hit.ray.direction, hit.normal);
color = doLighting(
hit.model,
hit.pos,
hit.normal,
ref,
hit.ray.direction
);
hit.color = color;
}
vec3 render(Hit hit) {
shadeSurface(hit);
return hit.color;
}
mat3 calcLookAtMatrix(in vec3 ro, in vec3 ta, in float roll) {
vec3 ww = normalize(ta - ro);
vec3 uu = normalize(cross(ww,vec3(sin(roll),cos(roll),0.0)));
vec3 vv = normalize(cross(uu,ww));
return mat3(uu, vv, ww);
}
void doCamera(out vec3 camPos, out vec3 camTar) {
float dist = 5.5;
camTar = vec3(0,0,0);
camPos = vec3(0,0,-dist);
camPos *= cameraRotation();
camPos += camTar;
}
const float GAMMA = 2.2;
vec3 gamma(vec3 color, float g) {
return pow(color, vec3(g));
}
vec3 linearToScreen(vec3 linearRGB) {
return gamma(linearRGB, 1.0 / GAMMA);
}
// 主函数--------------------------------------------------------
void main() {
time = uTime;
vec2 p = (-uResolution.xy + 2.0*gl_FragCoord.xy)/uResolution.y;
vec3 camPos = vec3(0., 0., 2.);
vec3 camTar = vec3(0., 0., 0.);
doCamera(camPos, camTar);
mat3 camMat = calcLookAtMatrix(camPos, camTar, 0.0);
vec3 rd = normalize(camMat * vec3(p.xy, 2.0));
Hit hit = raymarch(CastRay(camPos, rd));
vec3 color = render(hit);
color = linearToScreen(color);
gl_FragColor = vec4(color, 1.0);
}`;
最新发布