赞
踩
webgl是一种3D绘图协议,衍生于 OpenGL ES2.0,可以结合 Html5 和 JavaScript 在网页上绘制和渲染二/三维图形。
WebGL api 提供三维绘图的方式
图形的绘制主要通过 WebGLRenderingContext 接口完成
内嵌在浏览器中,不需要安装任何插件即可运⾏;
只需要⼀个⽂本编辑器和浏览器,就可以编写三维图形程序;
学习和使用比较简单
1.Three.js:JavaScript 3D WebGL库
2.Babylon.js:Web3D图形引擎
3.KickJS:Web的开源图形和游戏引擎
4.ClayGL:构建可扩展的Web3D应⽤程序
5.PlayCanvas:网络游戏和3D图形引擎
6.WebGLStudio.js和Litescene.js:开源Web 3D图形编辑器和创建器
7.Luma:Uber的3D WebGL可视化库
8.A-Frame是用于构建VR(虚拟现实)体验的Web框架
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 指定将要用来清空绘图区的颜色 清空颜色缓存 // gl.clearColor(r,g,b,a) 指定清空 <canvas> 的颜⾊,接收四个参数(取值区间为 0.0~1.0) gl.clearColor(1.0,0.0,0.0,1.0) // red 1.0 green 0.0 blue 0.0 alpha 1.0 // 使用之前指定的颜色,清空绘图区 gl.clear(gl.COLOR_BUFFER_BIT) </script>
attribute 变量只能在顶点着色器中使用,不能在片元着色器中使用
变量声明方法如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../lib/index.js"></script> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 创建着色器源码 const VERTEX_SHADER_SOURCE = ` // 只传递顶点数据 attribute vec4 aPosition; void main() { gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0) gl_PointSize = 30.0; } `; // 顶点着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `; // 片元着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) const aPosition = gl.getAttribLocation(program, 'aPosition'); // gl.vertexAttrib4f(aPosition, 0.5,0.5,0.0,1.0) // gl.vertexAttrib3f(aPosition, 0.5,0.5,0.0) // gl.vertexAttrib2f(aPosition, 0.5,0.5) let x = 0; setInterval(() => { x += 0.1; if (x > 1.0) { x = 0; } // 只改变x值 gl.vertexAttrib1f(aPosition, x) gl.drawArrays(gl.POINTS, 0, 1); }, 200) function initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) { const vertexShader = gl.createShader(gl.VERTEX_SHADER); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE) // 指定顶点着色器的源码 gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE) // 指定片元着色器的源码 // 编译着色器 gl.compileShader(vertexShader) gl.compileShader(fragmentShader) // 创建一个程序对象 const program = gl.createProgram(); gl.attachShader(program, vertexShader) gl.attachShader(program, fragmentShader) gl.linkProgram(program) gl.useProgram(program) return program; } </script>
添加 uniform 变量,设置到颜色上
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../lib/index.js"></script> <style> * { margin: 0; padding: 0; } canvas{ margin: 50px auto 0; display: block; background: yellow; } </style> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 创建着色器源码 const VERTEX_SHADER_SOURCE = ` uniform vec4 uPosition; // 只传递顶点数据 attribute vec4 aPosition; void main() { gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0) gl_PointSize = 10.0; } `; // 顶点着色器 // 顶点着色器需要指定精度,如下指定精度为中级精度 const FRAGMENT_SHADER_SOURCE = ` precision mediump float; uniform vec2 uColor; void main() { gl_FragColor = vec4(uColor.r, uColor.g, 0.0,1.0); // vec4 } `; // 片元着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) const aPosition = gl.getAttribLocation(program, 'aPosition'); const uColor = gl.getUniformLocation(program, 'uColor') const points = [] ctx.onclick = function(ev) { // 坐标 const x = ev.clientX const y = ev.clientY const domPosition = ev.target.getBoundingClientRect(); const domx = x - domPosition.left const domy = y - domPosition.top; // 当前画布宽度的一半 const halfWidth = ctx.offsetWidth / 2 // 当前画布高度的一半 const halfHeight = ctx.offsetHeight / 2 const clickX = (domx - halfWidth) / halfWidth const clickY = (halfHeight - domy) / halfHeight points.push({ clickX, clickY }) for (let i = 0; i < points.length; i++) { gl.vertexAttrib2f(aPosition, points[i].clickX, points[i].clickY) gl.uniform2f(uColor, points[i].clickX, points[i].clickY) gl.drawArrays(gl.POINTS, 0, 1); } } </script>
缓冲区对象是WebGL系统中的一块内存区域,可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。
buffer: 已经创建好的缓冲区对象
target:可以是如下两种
gl.ARRAY_BUFFER: 表示缓冲区存储的是顶点的数据
gl.ELEMENT_ARRAY_BUFFER: 表示缓冲区存储的是顶点的索引值
target: 类型同 gl.bindBuffer 中的 target
data: 写⼊缓冲区的顶点数据,如程序中的 points
type: 表示如何使⽤缓冲区对象中的数据,分为以下⼏类
location: attribute 变量的存储位置
size: 指定每个顶点所使⽤数据的个数
type: 指定数据格式
normalized: 表示是否将数据归⼀化到 [0, 1] [-1, 1] 这个区间
stride:两个相邻顶点之间的字节数
offset:数据偏移量
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../lib/index.js"></script> <style> * { margin: 0; padding: 0; } canvas{ margin: 50px auto 0; display: block; background: yellow; } </style> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 创建着色器源码 const VERTEX_SHADER_SOURCE = ` // 只传递顶点数据 attribute vec4 aPosition; void main() { gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0) gl_PointSize = 10.0; } `; // 顶点着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `; // 片元着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) const aPosition = gl.getAttribLocation(program, 'aPosition'); const points = new Float32Array([ -0.5, -0.5, 0.5, -0.5, 0.0, 0.5, ]) // 创建缓冲区对象 const buffer = gl.createBuffer(); // gl.ARRAY_BUFFER 缓冲区存储的是顶点数据 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); // 将数据写入缓冲区对象 gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);// 写入一次,多次绘制 // 将缓冲区对象分配给一个attribute变量 gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0); // 开启attribute变量 gl.enableVertexAttribArray(aPosition) // gl.vertexAttrib2f(aPosition, 0.0, 0.0) // 绘制什么图形,从哪个顶点开始绘制,需要绘制几个顶点 gl.drawArrays(gl.POINTS, 0, 3); </script>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../lib/index.js"></script> <style> * { margin: 0; padding: 0; } canvas{ margin: 50px auto 0; display: block; background: yellow; } </style> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 创建着色器源码 const VERTEX_SHADER_SOURCE = ` attribute vec4 aPosition; attribute float aPointSize; void main() { gl_Position = aPosition; gl_PointSize = aPointSize; } `; // 顶点着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `; // 片元着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) const aPosition = gl.getAttribLocation(program, 'aPosition'); const aPointSize = gl.getAttribLocation(program, 'aPointSize'); const points = new Float32Array([ -0.5, -0.5, 10.0, // 10.0 0.5, -0.5, 20.0, // 20.0 0.0, 0.5, 30.0, // 30.0 ]) const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); const BYTES = points.BYTES_PER_ELEMENT; // 两个数据之间的字节数是3。偏移0个字节取值。 gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, BYTES * 3, 0); gl.enableVertexAttribArray(aPosition) // 两个数据之间的字节数是3。偏移2个字节取值。 gl.vertexAttribPointer(aPointSize, 1, gl.FLOAT, false, BYTES * 3, BYTES * 2); gl.enableVertexAttribArray(aPointSize) gl.drawArrays(gl.POINTS, 0, 3); </script>
gl.drawArrays(mode, first, count)的mode参数其实十分强大,可以按照不同的规则绘制不同的图形,可直接绘制的图形有七种,这七种图形是绘制其它各种复杂图形的基础。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../lib/index.js"></script> <style> * { margin: 0; padding: 0; } canvas{ margin: 50px auto 0; display: block; background: yellow; } </style> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 创建着色器源码 const VERTEX_SHADER_SOURCE = ` attribute vec4 aPosition; attribute float aTranslate; void main() { gl_Position = vec4(aPosition.x + aTranslate, aPosition.y, aPosition.z, 1.0); gl_PointSize = 10.0; } `; // 顶点着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `; // 片元着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) const aPosition = gl.getAttribLocation(program, 'aPosition'); const aTranslate = gl.getAttribLocation(program, 'aTranslate'); const points = new Float32Array([ -0.5, -0.5, 0.5, -0.5, 0.0, 0.5, ]) const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPosition) let x = -1; setInterval(() => { x += 0.01; if (x > 1) { x = -1; } gl.vertexAttrib1f(aTranslate, x); gl.drawArrays(gl.TRIANGLES, 0, 3); }, 60) </script>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../lib/index.js"></script> <style> * { margin: 0; padding: 0; } canvas{ margin: 50px auto 0; display: block; background: yellow; } </style> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 创建着色器源码 const VERTEX_SHADER_SOURCE = ` attribute vec4 aPosition; attribute float aScale; void main() { gl_Position = vec4(aPosition.x * aScale, aPosition.y * aScale, aPosition.z * aScale, 1.0); gl_PointSize = 10.0; } `; // 顶点着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `; // 片元着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) const aPosition = gl.getAttribLocation(program, 'aPosition'); const aScale = gl.getAttribLocation(program, 'aScale'); const points = new Float32Array([ -0.5, -0.5, 0.5, -0.5, 0.0, 0.5, ]) const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPosition) let x = 1; setInterval(() => { x += 0.1; if (x > 2) { x = 1; } gl.vertexAttrib1f(aScale, x); gl.drawArrays(gl.TRIANGLES, 0, 3); }, 60) </script>
// 平移矩阵
function getTranslateMatrix(x = 0,y = 0,z = 0) {
return new Float32Array([
1.0,0.0,0.0,0.0,
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
x ,y ,z , 1,
])
}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../lib/index.js"></script> <style> * { margin: 0; padding: 0; } canvas{ margin: 50px auto 0; display: block; background: yellow; } </style> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 创建着色器源码 const VERTEX_SHADER_SOURCE = ` attribute vec4 aPosition; uniform mat4 mat; void main() { gl_Position = mat * aPosition; gl_PointSize = 10.0; } `; // 顶点着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `; // 片元着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) const aPosition = gl.getAttribLocation(program, 'aPosition'); const mat = gl.getUniformLocation(program, 'mat'); const points = new Float32Array([ -0.5, -0.5, 0.5, -0.5, 0.0, 0.5, ]) const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPosition) let x = -1; function animation() { x += 0.01; if (x > 1) { x = -1; } // getTranslateMatrix的x和y参数是x轴和y轴的偏移量 const matrix = getTranslateMatrix(x,x); // gl.vertexAttrib1f(aTranslate, x); gl.uniformMatrix4fv(mat, false, matrix); gl.drawArrays(gl.TRIANGLES, 0, 3); requestAnimationFrame(animation); } animation() </script>
// 缩放矩阵
function getScaleMatrix(x = 1,y = 1,z = 1) {
return new Float32Array([
x ,0.0,0.0,0.0,
0.0,y ,0.0,0.0,
0.0,0.0,z ,0.0,
0.0,0.0,0.0, 1,
])
}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../lib/index.js"></script> <style> * { margin: 0; padding: 0; } canvas{ margin: 50px auto 0; display: block; background: yellow; } </style> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> // 矩阵在数学中是按行,在webgl中是按列的,所以把数学中的矩阵沿着对角线做翻转 const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 创建着色器源码 // uniform对所有变量生效 const VERTEX_SHADER_SOURCE = ` attribute vec4 aPosition; uniform mat4 mat; void main() { gl_Position = mat * aPosition; gl_PointSize = 10.0; } `; // 顶点着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `; // 片元着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) const aPosition = gl.getAttribLocation(program, 'aPosition'); const mat = gl.getUniformLocation(program, 'mat'); const points = new Float32Array([ -0.5, -0.5, 0.5, -0.5, 0.0, 0.5, ]) const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPosition) let x = 0.1; function animation() { x += 0.01; if (x > 1.5) { x = 0.1; } // getScaleMatrix 的参数 x,y是x轴坐标和y轴坐标分别放大多少倍数; const matrix = getScaleMatrix(x,x); // gl.vertexAttrib1f(aTranslate, x); gl.uniformMatrix4fv(mat, false, matrix); gl.drawArrays(gl.TRIANGLES, 0, 3); requestAnimationFrame(animation); } animation() </script>
// 绕z轴旋转的旋转矩阵
function getRotateMatrix(deg) {
return new Float32Array([
Math.cos(deg) ,Math.sin(deg) ,0.0,0.0,
-Math.sin(deg) ,Math.cos(deg) ,0.0,0.0,
0.0, 0.0, 1.0,0.0,
0.0, 0.0, 0.0, 1,
])
}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../lib/index.js"></script> <style> * { margin: 0; padding: 0; } canvas{ margin: 50px auto 0; display: block; background: yellow; } </style> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> // 矩阵在数学中是按行,在webgl中是按列的,所以把数学中的矩阵沿着对角线做翻转 const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 创建着色器源码 // uniform对所有变量生效 const VERTEX_SHADER_SOURCE = ` attribute vec4 aPosition; uniform mat4 mat; void main() { gl_Position = mat * aPosition; gl_PointSize = 10.0; } `; // 顶点着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `; // 片元着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) const aPosition = gl.getAttribLocation(program, 'aPosition'); const mat = gl.getUniformLocation(program, 'mat'); const points = new Float32Array([ -0.5, -0.5, 0.5, -0.5, 0.0, 0.5, ]) const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPosition) let x = 0.1; function animation() { x += 0.01; if (x > 1.5) { x = 0.1; } // getScaleMatrix 的参数 x,y是x轴坐标和y轴坐标分别放大多少倍数; const matrix = getScaleMatrix(x,x); // gl.vertexAttrib1f(aTranslate, x); gl.uniformMatrix4fv(mat, false, matrix); gl.drawArrays(gl.TRIANGLES, 0, 3); requestAnimationFrame(animation); } animation() </script>
// 矩阵复合函数
function mixMatrix(A, B) {
const result = new Float32Array(16);
for (let i = 0; i < 4; i++) {
result[i] = A[i] * B[0] + A[i + 4] * B[1] + A[i + 8] * B[2] + A[i + 12] * B[3]
result[i + 4] = A[i] * B[4] + A[i + 4] * B[5] + A[i + 8] * B[6] + A[i + 12] * B[7]
result[i + 8] = A[i] * B[8] + A[i + 4] * B[9] + A[i + 8] * B[10] + A[i + 12] * B[11]
result[i + 12] = A[i] * B[12] + A[i + 4] * B[13] + A[i + 8] * B[14] + A[i + 12] * B[15]
}
return result;
}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../lib/index.js"></script> <style> * { margin: 0; padding: 0; } canvas{ margin: 50px auto 0; display: block; background: yellow; } </style> </head> <body> <canvas id="canvas" width="400" height="400"> 此浏览器不支持canvas </canvas> </body> </html> <script> const ctx = document.getElementById('canvas') const gl = ctx.getContext('webgl') // 创建着色器源码 const VERTEX_SHADER_SOURCE = ` attribute vec4 aPosition; uniform mat4 mat; void main() { gl_Position = mat * aPosition; } `; // 顶点着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `; // 片元着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) const aPosition = gl.getAttribLocation(program, 'aPosition'); const mat = gl.getUniformLocation(program, 'mat'); const points = new Float32Array([ -0.5, -0.5, 0.5, -0.5, 0.0, 0.5, ]) const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPosition) let deg = 0; let translateX = -1; let scaleX = 0.1; function animation() { deg += 0.01; translateX += 0.01; scaleX += 0.01; if (translateX > 1) { translateX = -1; } if (scaleX > 1.5) { scaleX = 0.1; } const translate = getTranslateMatrix(translateX); const scale = getScaleMatrix(scaleX); const rotate = getRotateMatrix(deg); const matrix = mixMatrix(mixMatrix(translate, scale), rotate) // gl.vertexAttrib1f(aTranslate, x); gl.uniformMatrix4fv(mat, false, matrix); gl.drawArrays(gl.TRIANGLES, 0, 3); requestAnimationFrame(animation); } animation() </script>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。