效果如下:

这个功能其实跟webgl 编程指南中的纹理demo一样的思路

思路是把相机放在镜子的位置,所形成材质,贴在镜子上面,这个有区别的是,它还计算了相机的位置,通过相机位置拿到与镜子镜像的相机位置,所形成的纹理;

demo 如下:

<!DOCTYPE html>
<html lang="en"><head><title>镜像shader编写</title><meta charset="utf-8"><meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"><style>body {color: #888888;font-family:Monospace;font-size:13px;background-color: #000;margin: 0px;overflow: hidden;}#info {position: absolute;top: 0px;width: 200px;left: calc(50% - 100px);text-align: center;}a {color: #00f;}</style></head><body><div id="container"></div><div id="info"></div><script src="./js/three.min.js"></script><script src="./js/Mirror.js"></script><script src="./js/controls/OrbitControls.js"></script><script>// scene sizevar WIDTH = window.innerWidth;var HEIGHT = window.innerHeight;// cameravar VIEW_ANGLE = 45;var ASPECT = WIDTH / HEIGHT;var NEAR = 1;var FAR = 500;var camera, scane, renderer;var cameraControls;var verticalMirror, groundMirror;var sphereGroup, smallSphere;function init() {// rendererrenderer = new THREE.WebGLRenderer();renderer.setSize( WIDTH, HEIGHT );renderer.autoClear = true;renderer.setClearColor( 0x000000, 1 );// scenescene = new THREE.Scene();// cameracamera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);camera.position.set( 0, 75, 160 );cameraControls = new THREE.OrbitControls(camera, renderer.domElement);cameraControls.target.set( 0, 40, 0);cameraControls.maxDistance = 400;cameraControls.minDistance = 10;cameraControls.update();var container = document.getElementById( 'container' );container.appendChild( renderer.domElement );}function fillScene() {var planeGeo = new THREE.PlaneGeometry( 100, 100 );//MIRORR planesgroundMirror = new THREE.Mirror( renderer, camera, { clipBias: 0.0, textureWidth: WIDTH, textureHeight: HEIGHT,debugMode:true} );var mirrorMesh = new THREE.Mesh( planeGeo, groundMirror.material );mirrorMesh.add( groundMirror );mirrorMesh.rotateX( - Math.PI / 2 );scene.add( mirrorMesh );verticalMirror = new THREE.Mirror( renderer, camera, { clipBias: 0.01, textureWidth: WIDTH, textureHeight: HEIGHT, color:0x889999 } );var verticalMirrorMesh = new THREE.Mesh( new THREE.PlaneGeometry( 60, 60 ), verticalMirror.material );verticalMirrorMesh.add( verticalMirror );verticalMirrorMesh.position.y = 35;verticalMirrorMesh.position.z = -45;scene.add( verticalMirrorMesh );sphereGroup = new THREE.Object3D();scene.add( sphereGroup );var geometry = new THREE.CylinderGeometry( 0.1, 15 * Math.cos( Math.PI / 180 * 30 ), 0.1, 24, 1 );var material = new THREE.MeshPhongMaterial( { color: 0xffffff, emissive: 0x444444 } );var sphereCap = new THREE.Mesh( geometry, material );sphereCap.position.y = -15 * Math.sin( Math.PI / 180 * 30 ) - 0.05;sphereCap.rotateX(-Math.PI);var geometry = new THREE.SphereGeometry( 15, 24, 24, Math.PI / 2, Math.PI * 2, 0, Math.PI / 180 * 120 );var halfSphere = new THREE.Mesh( geometry, material );halfSphere.add( sphereCap );halfSphere.rotateX( - Math.PI / 180 * 135 ); halfSphere.rotateZ( - Math.PI / 180 * 20 ); halfSphere.position.y = 7.5 + 15 * Math.sin( Math.PI / 180 * 30 );sphereGroup.add( halfSphere );var geometry = new THREE.IcosahedronGeometry( 5, 0 );var material = new THREE.MeshLambertMaterial( { color: 0xffffff, emissive: 0x333333, shading: THREE.FlatShading } );smallSphere = new THREE.Mesh( geometry, material );scene.add(smallSphere);    // wallsvar planeTop = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xffffff } ) );planeTop.position.y = 100;planeTop.rotateX( Math.PI / 2 );scene.add( planeTop );var planeBack = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xffffff } ) );planeBack.position.z = -50;planeBack.position.y = 50;scene.add( planeBack );var planeFront = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0x7f7fff } ) );planeFront.position.z = 50;planeFront.position.y = 50;planeFront.rotateY( Math.PI );scene.add( planeFront );var planeRight = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0x00ff00 } ) );planeRight.position.x = 50;planeRight.position.y = 50;planeRight.rotateY( - Math.PI / 2 );scene.add( planeRight );var planeLeft = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xff0000 } ) );planeLeft.position.x = -50;planeLeft.position.y = 50;planeLeft.rotateY( Math.PI / 2 );scene.add( planeLeft );// lightsvar mainLight = new THREE.PointLight( 0xcccccc, 1.5, 250 );mainLight.position.y = 60;scene.add( mainLight );var greenLight = new THREE.PointLight( 0x00ff00, 0.25, 1000 );greenLight.position.set( 550, 50, 0 );scene.add( greenLight );var redLight = new THREE.PointLight( 0xff0000, 0.25, 1000 );redLight.position.set( - 550, 50, 0 );scene.add( redLight );var blueLight = new THREE.PointLight( 0x7f7fff, 0.25, 1000 );blueLight.position.set( 0, 50, 550 );scene.add( blueLight );}function render() {// render (update) the mirrorsgroundMirror.renderWithMirror( verticalMirror );verticalMirror.renderWithMirror( groundMirror );//groundMirror.updateTextureMatrix();//groundMirror.render();//    verticalMirror.updateTextureMatrix();//verticalMirror.render();renderer.render(scene, camera);}function update() {requestAnimationFrame( update );var timer = Date.now() * 0.01;sphereGroup.rotation.y -= 0.002;smallSphere.position.set(Math.cos( timer * 0.1 ) * 30,Math.abs( Math.cos( timer * 0.2 ) ) * 20 + 5,Math.sin( timer * 0.1 ) * 30);smallSphere.rotation.y = ( Math.PI / 2 ) - timer * 0.1;smallSphere.rotation.z = timer * 0.8;cameraControls.update();render();}init();fillScene();update();</script></body>
</html>

主要的代码在Mirror.js中,如下:

/*** @author Slayvin / http://slayvin.net*/
//自定义着色器,这里定义一个镜像着色器
THREE.ShaderLib['mirror'] = {uniforms: {"mirrorColor": {type: "c", value: new THREE.Color(0x7F7F7F)},"mirrorSampler": {type: "t", value: null},"textureMatrix": {type: "m4", value: new THREE.Matrix4()}},vertexShader: ["uniform mat4 textureMatrix;","varying vec4 mirrorCoord;","void main() {","vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );","vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",//镜子所在的位置乘以纹理矩阵,得到纹理坐标"mirrorCoord = textureMatrix * worldPosition;",//这个给每个顶点gl_Position的赋值并没有什么特别,和普通着色器一样"gl_Position = projectionMatrix * mvPosition;","}"].join("\n"),fragmentShader: ["uniform vec3 mirrorColor;","uniform sampler2D mirrorSampler;","varying vec4 mirrorCoord;","float blendOverlay(float base, float blend) {","return( base < 0.5 ? ( 2.0 * base * blend ) : (1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );","}","void main() {",//得到纹理的颜色如果texture2DProj的 第二个参数(比如说是coord)是vec3类型,刚做如下变换coord.s /= coord.p 和coord.t/=coord.p//如果是vec4类型,则是coord.s /=coord.q 和 coord.t /= coord.q ;好像就是将它归一化再执行纹理查询;//texture2DProj:就是把纹理投射到场景的物体上,就像一个投影机把幻灯片投影到其他物体上;textureProj函数是用来访问纹理的(传入的纹理坐标是投影坐标系下的坐标),在平移缩放投影矩阵后,我们需要除以其坐标中的w项//而textureProj会为我们做这些;(https://blog.csdn.net/u012419410/article/details/41994117)//projtextcoord = 偏移矩阵 * 光源投影矩阵 * 光源观察矩阵 * 建模矩阵"vec4 color = texture2DProj(mirrorSampler, mirrorCoord);",//纹理的颜色和镜子本来的颜色进行混合,得到最终的纹理颜色"color = vec4(blendOverlay(mirrorColor.r, color.r), blendOverlay(mirrorColor.g, color.g), blendOverlay(mirrorColor.b, color.b), 1.0);","gl_FragColor = color;","}"].join("\n")};THREE.Mirror = function (renderer, camera, options) {THREE.Object3D.call(this);//继承Threejs 的Object3D,得到相关函数//生成一个名字this.name = 'mirror_' + this.id;//判断value值是不是2的次方,纹理的宽高最好是2的次方;有些硬件不支持,也便于运算速度function isPowerOfTwo(value) {return ( value & ( value - 1 ) ) === 0;};//如果没有传入参数,那么option为{}options = options || {};//是否更新矩阵this.matrixNeedsUpdate = true;//纹理的高度和宽度,默认为512var width = options.textureWidth !== undefined ? options.textureWidth : 512;var height = options.textureHeight !== undefined ? options.textureHeight : 512;//剪切this.clipBias = options.clipBias !== undefined ? options.clipBias : 0.0;//镜像颜色var mirrorColor = options.color !== undefined ? new THREE.Color(options.color) : new THREE.Color(0x7F7F7F);this.renderer = renderer;this.mirrorPlane = new THREE.Plane();//平面this.normal = new THREE.Vector3(0, 0, 1);this.mirrorWorldPosition = new THREE.Vector3();//镜面位置this.cameraWorldPosition = new THREE.Vector3();//相机位置this.rotationMatrix = new THREE.Matrix4();//旋转矩阵this.lookAtPosition = new THREE.Vector3(0, 0, -1);this.clipPlane = new THREE.Vector4();//剪切平面// 如果调试,将显示一个调试的plane在场景中var debugMode = options.debugMode !== undefined ? options.debugMode : false;//调试用的平面和箭头if (debugMode) {var arrow = new THREE.ArrowHelper(new THREE.Vector3(0, 0, 1), new THREE.Vector3(0, 0, 0), 10, 0xffff80);var planeGeometry = new THREE.Geometry();planeGeometry.vertices.push(new THREE.Vector3(-10, -10, 0));planeGeometry.vertices.push(new THREE.Vector3(10, -10, 0));planeGeometry.vertices.push(new THREE.Vector3(10, 10, 0));planeGeometry.vertices.push(new THREE.Vector3(-10, 10, 0));planeGeometry.vertices.push(planeGeometry.vertices[0]);var plane = new THREE.Line(planeGeometry, new THREE.LineBasicMaterial({color: 0xffff80}));this.add(arrow);this.add(plane);}//是否使用透视相机,没有就创建一个if (camera instanceof THREE.PerspectiveCamera) {this.camera = camera;} else {this.camera = new THREE.PerspectiveCamera();console.log(this.name + ': camera is not a Perspective Camera!');}//初始化纹理矩阵this.textureMatrix = new THREE.Matrix4();//复制一个相机,主要是得到相机的一些参数this.mirrorCamera = this.camera.clone();//WebGLRenderTarget对应的是帧缓存,就是屏幕中显示的一帧在内存中的表示,这里声明两个帧缓存,主要是用来加快动画的切换速度this.texture = new THREE.WebGLRenderTarget(width, height);this.tempTexture = new THREE.WebGLRenderTarget(width, height);//没有一种内置的着色器能够完成光线最终的效果,所以,这里我们必须自己定义一个着色器,var mirrorShader = THREE.ShaderLib["mirror"];var mirrorUniforms = THREE.UniformsUtils.clone(mirrorShader.uniforms);//定义一个着色器材质,传入顶点和片元着色器this.material = new THREE.ShaderMaterial({fragmentShader: mirrorShader.fragmentShader,vertexShader: mirrorShader.vertexShader,uniforms: mirrorUniforms});//向材质着色器传入变量,将纹理传入着色器中this.material.uniforms.mirrorSampler.value = this.texture;//镜子的颜色this.material.uniforms.mirrorColor.value = mirrorColor;//传入纹理矩阵this.material.uniforms.textureMatrix.value = this.textureMatrix;//如果纹理的宽高不是2的次方,那么久不生成mipmap贴图if (!isPowerOfTwo(width) || !isPowerOfTwo(height)) {this.texture.generateMipmaps = false;this.tempTexture.generateMipmaps = false;}//更新纹理矩阵this.updateTextureMatrix();//渲染纹理,将纹理渲染到texture 或temTexture中this.render();};THREE.Mirror.prototype = Object.create(THREE.Object3D.prototype);
//镜子渲染
THREE.Mirror.prototype.renderWithMirror = function (otherMirror) {// 更新镜像矩阵以镜像当前视图为纹理this.updateTextureMatrix();this.matrixNeedsUpdate = false;// 设置另一面镜子的相机,这样镜像视图就是参考视图var tempCamera = otherMirror.camera;otherMirror.camera = this.mirrorCamera;// 用临时纹理渲染另一面镜子otherMirror.renderTemp();otherMirror.material.uniforms.mirrorSampler.value = otherMirror.tempTexture;// render the current mirrorthis.render();this.matrixNeedsUpdate = true;// restore material and camera of other mirrorotherMirror.material.uniforms.mirrorSampler.value = otherMirror.texture;otherMirror.camera = tempCamera;// restore texture matrix of other mirrorotherMirror.updateTextureMatrix();
};
//更新纹理函数
THREE.Mirror.prototype.updateTextureMatrix = function () {//Three.math.sign 这个函数用来返回一个数的符号,var sign = THREE.Math.sign;//更新世界矩阵,也就是更新THREE.Mirror 的位置,THREE.Mirror中提供了镜子的纹理和调试时的显示的平面和箭头,//所以需要对其位置进行实时更新,那么平面和箭头的位置才能够实时更新;this.updateMatrixWorld();//更新相机的位置,这个相机是全局的相机this.camera.updateMatrixWorld();//从世界矩阵中得到目前镜子所在的位置this.mirrorWorldPosition.getPositionFromMatrix(this.matrixWorld);//得到相机目前所在的位置this.cameraWorldPosition.getPositionFromMatrix(this.camera.matrixWorld);//从镜子的世界矩阵中计算出其旋转矩阵出来。世界矩阵能够表达位移,旋转,缩放,所以能够单独的提取出旋转矩阵出来this.rotationMatrix.extractRotation(this.matrixWorld);//设置镜子的法向量this.normal.set(0, 0, 1);//法向量乘上旋转矩阵;(物体的旋转和放大缩小,位移,它们的法向量只与旋转矩阵有关)this.normal.applyMatrix4(this.rotationMatrix);//计算摄像机指向镜子的向量var view = this.mirrorWorldPosition.clone().sub(this.cameraWorldPosition);//根据法线,计算出view的反射向量var reflectView = view.reflect(this.normal);//计算出反射向量,并根据镜子的位置得出向量的结果,最终得到摄像机反射后应该所在的位置reflectView.add(this.mirrorWorldPosition);//从相机的世界矩阵得到相机的旋转矩阵this.rotationMatrix.extractRotation(this.camera.matrixWorld);//对相机的目标点向量也进行旋转,并加到相机目前的位置,即可得相机的目标点应该所在的位置this.lookAtPosition.set(0, 0, -1);this.lookAtPosition.applyMatrix4(this.rotationMatrix);this.lookAtPosition.add(this.cameraWorldPosition);//对目标进行镜像反射,得到位置var target = this.mirrorWorldPosition.clone().sub(this.lookAtPosition);var reflectTarget = target.reflect(this.normal);reflectTarget.add(this.mirrorWorldPosition);this.up.set(0, -1, 0);this.up.applyMatrix4(this.rotationMatrix);var reflectUp = this.up.reflect(this.normal);//镜子中的场景是一个单独的摄像机来实现的,这个相机的位置是reflectViewthis.mirrorCamera.position.copy(reflectView);//相机的上方向this.mirrorCamera.up = reflectUp;//相机所看到的位置this.mirrorCamera.lookAt(reflectTarget);//更新相机的投影矩阵this.mirrorCamera.updateProjectionMatrix();//更新相机的世界矩阵this.mirrorCamera.updateMatrixWorld();//相机的投影视图矩阵的逆矩阵this.mirrorCamera.matrixWorldInverse.getInverse(this.mirrorCamera.matrixWorld);// 将纹理缩小0.5,这样就不会将这个场景映射到镜子中了;this.textureMatrix.set(0.5, 0.0, 0.0, 0.5,0.0, 0.5, 0.0, 0.5,0.0, 0.0, 0.5, 0.5,0.0, 0.0, 0.0, 1.0);//纹理矩阵乘以投影矩阵,最后得到在屏幕中显示的矩阵,及投影视图矩阵,this.textureMatrix.multiply(this.mirrorCamera.projectionMatrix);this.textureMatrix.multiply(this.mirrorCamera.matrixWorldInverse);//用现在新的剪切算法更新矩阵, 可以看这个: http://www.terathon.com/code/oblique.html//相关论文: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf//以下代码去掉都不会影响最终的效果,这里只是让剪切效果更好一些this.mirrorPlane.setFromNormalAndCoplanarPoint(this.normal, this.mirrorWorldPosition);this.mirrorPlane.applyMatrix4(this.mirrorCamera.matrixWorldInverse);this.clipPlane.set(this.mirrorPlane.normal.x, this.mirrorPlane.normal.y, this.mirrorPlane.normal.z, this.mirrorPlane.constant);var q = new THREE.Vector4();var projectionMatrix = this.mirrorCamera.projectionMatrix;q.x = ( sign(this.clipPlane.x) + projectionMatrix.elements[8] ) / projectionMatrix.elements[0];q.y = ( sign(this.clipPlane.y) + projectionMatrix.elements[9] ) / projectionMatrix.elements[5];q.z = -1.0;q.w = ( 1.0 + projectionMatrix.elements[10] ) / projectionMatrix.elements[14];// Calculate the scaled plane vectorvar c = new THREE.Vector4();c = this.clipPlane.multiplyScalar(2.0 / this.clipPlane.dot(q));// Replacing the third row of the projection matrixprojectionMatrix.elements[2] = c.x;projectionMatrix.elements[6] = c.y;projectionMatrix.elements[10] = c.z + 1.0 - this.clipBias;projectionMatrix.elements[14] = c.w;};THREE.Mirror.prototype.render = function () {//更新纹理矩阵if (this.matrixNeedsUpdate) this.updateTextureMatrix();//是否更新矩阵this.matrixNeedsUpdate = true;// 将当前场景的镜像渲染到目标纹理texture中var scene = this;//找到父场景,这里是主场景中可以添加子场景while (scene.parent !== undefined) {scene = scene.parent;}if (scene !== undefined && scene instanceof THREE.Scene) {//通过相机和场景,将帧渲染到texture中/*render(scene,camera,renderTarget,forceClear)* scene :场景对象* camera : 相机对象* renderTarget : 渲染目标* forceClear :是否强制清屏* */this.renderer.render(scene, this.mirrorCamera, this.texture, true);}};
//临时渲染
THREE.Mirror.prototype.renderTemp = function () {if (this.matrixNeedsUpdate) this.updateTextureMatrix();this.matrixNeedsUpdate = true;// 将当前场景的镜像视图渲染为目标纹理var scene = this;while (scene.parent !== undefined) {scene = scene.parent;}if (scene !== undefined && scene instanceof THREE.Scene) {this.renderer.render(scene, this.mirrorCamera, this.tempTexture, true);}};

转载请说明来源:https://mp.csdn.net/postedit

Threejs 官网镜子demo源码分析相关推荐

  1. 超好看的Nteam官网PHP程序源码

    NanGe Nteam 该项目适用于团队/工作室等类型,全站由layui强力驱动,及光年后台模板的使用 团队介绍.项目展示.成员列表等 成员申请.作品申请.成员真假查询.人机验证 多管理员.成员.项目 ...

  2. WeChat苹果多开系统官网下载页源码

    WeChat苹果多开系统官网下载页源码 学习资料文件:导航源.zip - 蓝奏云

  3. HTML+CSS期末大作业:在线音乐网站设计——简洁大气的KK音乐官网模板html源码(1页) HTML+CSS+JavaScript

    HTML+CSS期末大作业:在线音乐网站设计--简洁大气的KK音乐官网模板html源码(1页) HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕 ...

  4. 基于JAVA桂林恒保健康防护有限公司官网计算机毕业设计源码+数据库+lw文档+系统+部署

    基于JAVA桂林恒保健康防护有限公司官网计算机毕业设计源码+数据库+lw文档+系统+部署 基于JAVA桂林恒保健康防护有限公司官网计算机毕业设计源码+数据库+lw文档+系统+部署 本源码技术栈: 项目 ...

  5. HTML5期末大作业:直播网站设计——仿在线媒体歪秀直播官网模板html源码(11个页面) HTML+CSS+JavaScript 期末作业HTML代码

    HTML5期末大作业:直播网站设计--仿在线媒体歪秀直播官网模板html源码(11个页面) HTML+CSS+JavaScript 期末作业HTML代码 临近期末, 你还在为HTML网页设计结课作业, ...

  6. Pytorch Mobile 之Android Demo源码分析

    现如今,在边缘设备上运行机器学习/深度学习变得越来越流行,它需要更低的时延. 而从Pytorch 1.3开始,我们就可以使用Pytorch将模型部署到Android或者ios设备中. Pytorch官 ...

  7. 如何在Spring官网下载Spring源码包

    在编写代码的过程中,我们常常会有需要观看源代码的需求,但是可能事先并没有需要的源码包,那么该如何重从官网导入需要的源码包呢?下面简述一种导入源码包的方式: 1.点击查看源码时,提示源码不存在,则我们需 ...

  8. vitamio官方demo源码分析(1)——MediaPlayerDemo_Video.java分析

    最近在做一个视频监控项目的android客户端,要求用rtsp协议完成视频流的传输,但苦于找到不合适的库.之前考虑过用live555或ffmpeg,但涉及到jni调用,加之不熟悉函数调用顺序,开发难度 ...

  9. mysql官网如何下载源码包_mysql官网如何下载源码包?

    我就废话不多说了,大家还是直接看代码吧~create or replace function aa1(a1 integer[],a2 bigint) returns void AS $$declare ...

最新文章

  1. 三,ES6中需要注意的特性(重要)
  2. 维护窗口和停机时间 可用率99.99%
  3. sqlserver linkserver
  4. Jenkins+Gradle+Git+Pyger+二维码搭建Android自动打包平台
  5. 20211108 det(AB)=det(A)det(B)
  6. Sharepoint学习笔记—ECM系列--根据位置设置的默认元数据值(Location-Based Metadata Defaults)...
  7. 掌握rm命令删除文件
  8. 熊猫烧香病毒逆向分析
  9. 《数据可视化》之小白学习篇(二)
  10. otdr进行多种测试鸿蒙包括,OTDR 测试
  11. 刘铁猛-深入浅出WPF-系列资源汇总
  12. 计算机毕业设计Python+django 网上外卖订餐系统(源码+系统+mysql数据库+Lw文档)
  13. P2184 贪婪大陆(线段树)
  14. 游戏开发物理引擎PhysX研究系列:通过Unity中的物理系统学习Physx指引贴
  15. android删除自带应用程序,安卓手机自带软件怎么卸载?无需root卸载安卓手机自带软件方法...
  16. Python OpenCV:利用滚动条移动图片,利用鼠标缩放图片
  17. 跨境电商如何找代运营公司?星之河
  18. Element UI是什么?基本用法
  19. 小程序--语音合成tts 对接多平台(讯飞,思必驰,百度)
  20. 计算机网络试卷模板,试卷模板格式..doc

热门文章

  1. ARP欺骗之——ARP攻击防范
  2. springboot集成Actuator
  3. 递归实现类似计算器的公式计算处理工资,公积金,社保计算
  4. 弹出启动windows安全中心服务器,如何解决Win7系统无法启动Windows安全中心的问题?...
  5. 全国列车时刻表(手机版)
  6. 如何使夜游项目更具有参观性
  7. strang thought
  8. 海思3531下的mp4视频播放器
  9. 软件测试专业应届生应如何提高职场竞争力
  10. PCL-surface/on_nurbs模块分析